#include "mainwindow.h"
#include "profilereditor.h"
#include "astyleoptiondialog.h"
#include "editoroptiondialog.h"
#include "colorthemedialog.h"
#include "compileoptiondialog.h"
#include "newprojectdialog.h"
#include "globalvar.h"
#include "manualdialog.h"
#include "informationdialog.h"
#include "searchresultdelegate.h"
#include "richlinecode.h"

struct SearchResult
{
    QString fileName;
    int row;
    int column;
    QVariant richLine;
};

struct CompileResult
{
    QString fileName;
    QString row;
    QString column;
    QString type;
    QString message;
};

MainWindow::MainWindow(QWidget *parent): QMainWindow(parent)
{
    darkPalette.setColor(QPalette::Window, QColor(56, 56, 56));
    darkPalette.setColor(QPalette::WindowText, QColor(232, 232, 232));
    darkPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
    darkPalette.setColor(QPalette::Base, QColor(0, 0, 0));
    darkPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(45, 45, 45));
    darkPalette.setColor(QPalette::AlternateBase, QColor(45, 45, 45));
    darkPalette.setColor(QPalette::ToolTipBase, QColor(72, 72, 72));
    darkPalette.setColor(QPalette::ToolTipText, Qt::white);
    darkPalette.setColor(QPalette::Text, QColor(232, 232, 232));
    darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(128, 128, 128));
    darkPalette.setColor(QPalette::Button, QColor(66, 66, 66));
    //darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(32,32,32));
    darkPalette.setColor(QPalette::ButtonText, QColor(232, 232, 232));
    darkPalette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray);
    darkPalette.setColor(QPalette::BrightText, Qt::red);
    darkPalette.setColor(QPalette::Link, QColor(42, 130, 218));
    darkPalette.setColor(QPalette::Highlight, QColor(160, 220, 240));
    darkPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
    darkPalette.setColor(QPalette::HighlightedText, Qt::black);
    darkPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
    //darkPalette.setColor(QPalette::Dark,QColor(192,192,192));
    //darkPalette.setColor(QPalette::Shadow,QColor(192,192,192));
    //darkPalette.setColor(QPalette::Light,QColor(255,255,192));

    grayPalette.setColor(QPalette::Window, QColor(46, 56, 66));
    grayPalette.setColor(QPalette::WindowText, QColor(232, 232, 232));
    grayPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
    grayPalette.setColor(QPalette::Base, QColor(32, 32, 32));
    grayPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(56, 56, 56));
    grayPalette.setColor(QPalette::AlternateBase, QColor(56, 56, 56));
    grayPalette.setColor(QPalette::ToolTipBase, QColor(72, 72, 72));
    grayPalette.setColor(QPalette::ToolTipText, QColor(212, 212, 212));
    grayPalette.setColor(QPalette::Text, QColor(224, 224, 224));
    grayPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(128, 128, 128));
    grayPalette.setColor(QPalette::Button, QColor(68, 68, 68));
    //grayPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(32,32,32));
    grayPalette.setColor(QPalette::ButtonText, QColor(232, 232, 232));
    grayPalette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray);
    grayPalette.setColor(QPalette::BrightText, Qt::red);
    grayPalette.setColor(QPalette::Link, QColor(42, 130, 218));
    grayPalette.setColor(QPalette::Highlight, QColor(160, 220, 240));
    grayPalette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
    grayPalette.setColor(QPalette::HighlightedText, Qt::black);
    grayPalette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));

    lightPalette.setColor(QPalette::Window, QColor(240, 240, 240));
    lightPalette.setColor(QPalette::WindowText, Qt::black);
    lightPalette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
    lightPalette.setColor(QPalette::Base, QColor(255, 255, 255));
    lightPalette.setColor(QPalette::Disabled, QPalette::Base, QColor(240, 240, 240));
    lightPalette.setColor(QPalette::AlternateBase, QColor(210, 210, 210));
    lightPalette.setColor(QPalette::ToolTipBase, QColor(240, 240, 240));
    lightPalette.setColor(QPalette::ToolTipText, Qt::black);
    lightPalette.setColor(QPalette::Text, Qt::black);
    lightPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(128, 128, 128));
    lightPalette.setColor(QPalette::Button, QColor(240, 240, 240));
    //lightPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(245,245,245));
    lightPalette.setColor(QPalette::ButtonText, Qt::black);
    lightPalette.setColor(QPalette::Disabled, QPalette::ButtonText, Qt::gray);
    lightPalette.setColor(QPalette::BrightText, Qt::red);
    lightPalette.setColor(QPalette::Highlight, QColor(0, 120, 200));
    lightPalette.setColor(QPalette::HighlightedText, Qt::white);

    codeFileSuffixList<<"c"<<"cpp"<<"cc"<<"cxx"<<"h"<<"hxx"<<"hpp"<<"rc";

    //从设置文件Setting.ini中读取上次窗口关闭时的尺寸和状态
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    this->lastWindowState = setting.value("LastWindowState", 0).toInt();
    int windowWidth = setting.value("LastWindowWidth", 1280).toInt();
    int windowHeight = setting.value("LastWindowHeight", 720).toInt();
    isAutoFormat = setting.value("AutoFormatCheckBox", true).toBool();
    toolBarIconSize = setting.value("IconSize", 28).toInt();
    encodingIndex = setting.value("EncodingIndex", 0).toInt();
    compileMode = setting.value("CompileMode","Release").toString();
    GlobalVar::colorTheme = setting.value("ColorTheme", 0).toInt();
    if (GlobalVar::colorTheme < 0) GlobalVar::colorTheme = 0;
    else if (GlobalVar::colorTheme > 2) GlobalVar::colorTheme = 2;

    bool showToolBar=setting.value("ShowToolBar", true).toBool();
    bool showStatusBar=setting.value("ShowStatusBar", true).toBool();

    GlobalVar::keywordColor = setting.value("keywordColor").value<QList<QColor>>();
    if (GlobalVar::keywordColor.count()!=3) GlobalVar::keywordColor=GlobalVar::defaultKeywordColor;

    GlobalVar::functionColor = setting.value("functionColor").value<QList<QColor>>();
    if (GlobalVar::functionColor.count()!=3) GlobalVar::functionColor=GlobalVar::defaultFunctionColor;

    GlobalVar::numberColor = setting.value("numberColor").value<QList<QColor>>();
    if (GlobalVar::numberColor.count()!=3) GlobalVar::numberColor=GlobalVar::defaultNumberColor;

    GlobalVar::symbolColor = setting.value("symbolColor").value<QList<QColor>>();
    if (GlobalVar::symbolColor.count()!=3) GlobalVar::symbolColor=GlobalVar::defaultSymbolColor;

    GlobalVar::qtClassColor = setting.value("qtClassColor").value<QList<QColor>>();
    if (GlobalVar::qtClassColor.count()!=3) GlobalVar::qtClassColor=GlobalVar::defaultQtClassColor;

    GlobalVar::preprocessColor = setting.value("preprocessColor").value<QList<QColor>>();
    if (GlobalVar::preprocessColor.count()!=3) GlobalVar::preprocessColor=GlobalVar::defaultPreprocessColor;

    GlobalVar::commentColor = setting.value("commentColor").value<QList<QColor>>();
    if (GlobalVar::commentColor.count()!=3) GlobalVar::commentColor=GlobalVar::defaultCommentColor;

    GlobalVar::stringColor = setting.value("stringColor").value<QList<QColor>>();
    if (GlobalVar::stringColor.count()!=3) GlobalVar::stringColor=GlobalVar::defaultStringColor;

    GlobalVar::editorForeground = setting.value("editorForeground").value<QList<QColor>>();
    if (GlobalVar::editorForeground.count()!=3) GlobalVar::editorForeground=GlobalVar::defaultEditorForeground;

    QList<CompilerSet> compilerSetList;
    compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
    if (compilerSetList.count()==0)
        CompileOptionDialog compileOptionDialog;

    this->setMinimumSize(QSize(320, 200));
    this->resize(windowWidth, windowHeight);
    this->setAcceptDrops(true); //设置本窗口允许接收Drop操作
    this->setWindowTitle("Cute C++");
    this->setWindowIcon(QPixmap(":/images/cute-cpp.png"));

    yesPixmap = QPixmap(":images/yes.png");

    menuBar = new QMenuBar;

    fileMenu = new QMenu(tr("&File"));
    editMenu = new QMenu(tr("&Edit"));
    searchMenu = new QMenu(tr("&Search"));
    viewMenu = new QMenu(tr("&View"));
    projectMenu = new QMenu(tr("&Project"));
    executeMenu = new QMenu(tr("E&xecute"));
    toolsMenu = new QMenu(tr("&Tools"));
    aStyleMenu = new QMenu("ASt&yle");
    windowMenu = new QMenu(tr("&Window"));
    helpMenu = new QMenu(tr("&Help"));

    projectContextMenu=new QMenu(tr("Project Menu"),this);
    itemContextMenu=new QMenu(tr("Item Menu"),this);

    menuList={fileMenu, editMenu, searchMenu, viewMenu, projectMenu, executeMenu, toolsMenu, aStyleMenu, windowMenu, helpMenu, projectContextMenu, itemContextMenu};

    this->setMenuBar(menuBar);

    menuBar->addMenu(fileMenu);

    connect(fileMenu, &QMenu::aboutToShow, this, &MainWindow::fileMenuAboutToShow);

    newAction = fileMenu->addAction(QPixmap(":/images/new.png"), tr("New File"), QKeySequence::New, this, &MainWindow::newFile);
    openAction = fileMenu->addAction(QPixmap(":/images/open.png"), tr("Open file"), QKeySequence::Open, this, &MainWindow::openFile);
    fileMenu->addSeparator();
    saveAction = fileMenu->addAction(QPixmap(":/images/save.png"), tr("Save file"), QKeySequence::Save, this, &MainWindow::saveFileActionClicked);
    saveAction->setEnabled(false);
    saveAsAction = fileMenu->addAction(QPixmap(":/images/saveAs.png"), tr("Save As"), this, &MainWindow::saveAsFileActionClicked);
    saveAsAction->setEnabled(false);
    fileMenu->addSeparator();

    exportMenu = fileMenu->addMenu(QPixmap(":/images/export.png"),tr("Export"));
    exportDocAction = exportMenu->addAction(tr("to Word"), this, &MainWindow::exportFile);
    exportHTMLAction = exportMenu->addAction(tr("to HTML"), this, &MainWindow::exportFile);
    exportMenu->setEnabled(false);

    fileMenu->addSeparator();
    printAction = fileMenu->addAction(QPixmap(":/images/print.png"), tr("Print"), this, &MainWindow::print);
    printAction->setEnabled(false);
    fileMenu->addSeparator();

    historyMenu = fileMenu->addMenu(tr("History List"));
    historyMenu->addSeparator();
    clearRecentAction = historyMenu->addAction(tr("Clear History"), this, &MainWindow::clearRecentActions);
    fileMenu->addSeparator();
    exitAction =fileMenu->addAction(QPixmap(":/images/exit.png"), tr("Exit"), this, &MainWindow::appExit);

    menuBar->addMenu(editMenu);

    undoAction = editMenu->addAction(QPixmap(":/images/undo.png"), tr("Undo"), QKeySequence::Undo, this, &MainWindow::undo);
    redoAction = editMenu->addAction(QPixmap(":/images/redo.png"), tr("Redo"), QKeySequence::Redo, this, &MainWindow::redo);
    editMenu->addSeparator();
    cutAction = editMenu->addAction(QPixmap(":/images/cut.png"), tr("Cut"), QKeySequence::Cut, this, &MainWindow::cut);
    copyAction = editMenu->addAction(QPixmap(":/images/copy.png"), tr("Copy"), QKeySequence::Copy, this, &MainWindow::copy);
    pasteAction = editMenu->addAction(QPixmap(":/images/paste.png"), tr("Paste"), QKeySequence::Paste, this, &MainWindow::paste);
    selectAllAction = editMenu->addAction(QPixmap(":/images/selectAll.png"), tr("Select All"), QKeySequence::SelectAll, this, &MainWindow::selectAll);
    deleteAction = editMenu->addAction(QPixmap(":/images/delete.png"), tr("Delete Selection"), QKeySequence::Delete, this, &MainWindow::deleteSelection);
    editMenu->addSeparator();
    copyAsHtmlAction = editMenu->addAction(QPixmap(":/images/html.png"), tr("Copy As Html"), this, &MainWindow::copyAsHtml);
    editMenu->addSeparator();
    snippetMenu = editMenu->addMenu(QPixmap(":/images/insertSnippet.png"), tr("Insert Snippet"));
    snippetMenu->addAction("C main", this, &MainWindow::insertCMain);
    snippetMenu->addAction("C++ main", this, &MainWindow::insertCppMain);
    snippetMenu->addAction("C++&&Qt main", this, &MainWindow::insertCppQtMain);
    snippetMenu->setEnabled(false);
    editMenu->addSeparator();
    indentAction = editMenu->addAction(QPixmap(":/images/indent.png"), tr("Indent"), QKeySequence(Qt::Key_Tab), this, &MainWindow::indent);
    unindentAction = editMenu->addAction(QPixmap(":/images/unindent.png"), tr("Unindent"), QKeySequence(Qt::SHIFT | Qt::Key_Tab), this, &MainWindow::unindent);
    editMenu->addSeparator();
    commentOnOffAction = editMenu->addAction(QPixmap(":/images/comment.png"), tr("Toggle Comment"), QKeySequence(Qt::CTRL | Qt::Key_Slash), this, &MainWindow::commentOnOff);

    foreach (QAction *action, editMenu->actions())
        action->setEnabled(false);

    menuBar->addMenu(searchMenu);

    findAction = searchMenu->addAction(QPixmap(":/images/search.png"), tr("Find"), QKeySequence::Find, this, &MainWindow::find);
    replaceAction = searchMenu->addAction(QPixmap(":/images/replace.png"), tr("Replace"), this, &MainWindow::replace);
    searchMenu->addSeparator();
    findPreviousAction = searchMenu->addAction(QPixmap(":/images/findprevious.png"), tr("Find Previous"), QKeySequence("F8"), this, &MainWindow::findPreviousNext);
    findNextAction = searchMenu->addAction(QPixmap(":/images/findnext.png"), tr("Find Next"), QKeySequence("F9"), this, &MainWindow::findPreviousNext);
    searchMenu->addSeparator();
    gotoLineAction = searchMenu->addAction(QPixmap(":/images/gotoLine.png"), tr("Goto Line"), QKeySequence(Qt::CTRL | Qt::Key_G), this, &MainWindow::gotoLineActionClicked);

    foreach (QAction *action, searchMenu->actions())
        action->setEnabled(false);

    menuBar->addMenu(viewMenu);

    zoomInAction = viewMenu->addAction(QPixmap(":/images/zoomin.png"), tr("Zoom In"), QKeySequence::ZoomIn, this, &MainWindow::zoom);
    zoomInAction->setEnabled(false);
    zoomOutAction = viewMenu->addAction(QPixmap(":/images/zoomout.png"), tr("Zoom Out"), QKeySequence::ZoomOut, this, &MainWindow::zoom);
    zoomOutAction->setEnabled(false);
    viewMenu->addSeparator();

    toolBarAction = viewMenu->addAction(showToolBar?yesPixmap:QPixmap(), tr("ToolBar"), this, &MainWindow::toolBarOnOff);
    statusBarAction = viewMenu->addAction(showStatusBar?yesPixmap:QPixmap(), tr("StatusBar"), this, &MainWindow::statusBarOnOff);

    viewMenu->addSeparator();
    toolBarDockMenu = viewMenu->addMenu(QPixmap(":/images/dock.png"), tr("ToolBar Dock"));
    toolBarDockMenu->setEnabled(showToolBar);
    dockTopAction = toolBarDockMenu->addAction(tr("Top"), this, &MainWindow::changeToolBarDock);
    dockBottomAction = toolBarDockMenu->addAction(tr("Bottom"), this, &MainWindow::changeToolBarDock);
    dockLeftAction = toolBarDockMenu->addAction(tr("Left"), this, &MainWindow::changeToolBarDock);
    dockRightAction = toolBarDockMenu->addAction(tr("Right"), this, &MainWindow::changeToolBarDock);
    viewMenu->addSeparator();
    colorThemeOptionAction=viewMenu->addAction(QPixmap(":/images/colorThemeOption.png"), tr("Syntax Highlight Customize"), this, &MainWindow::colorThemeOptions);

    colorThemeMenu = viewMenu->addMenu(QPixmap(":/images/colorTheme.png"), tr("Select Color Theme"));

    lightThemeAction = colorThemeMenu->addAction(tr("White"), this, &MainWindow::changeColorTheme);
    grayThemeAction = colorThemeMenu->addAction(tr("Gray"), this, &MainWindow::changeColorTheme);
    darkThemeAction = colorThemeMenu->addAction(tr("Black"), this, &MainWindow::changeColorTheme);

    lightThemeAction->setIcon(QPixmap());
    grayThemeAction->setIcon(QPixmap());
    darkThemeAction->setIcon(QPixmap());
    if (GlobalVar::colorTheme == 0)
        lightThemeAction->setIcon(yesPixmap);
    else if (GlobalVar::colorTheme == 1)
        grayThemeAction->setIcon(yesPixmap);
    else
        darkThemeAction->setIcon(yesPixmap);

    connect(toolBarDockMenu, &QMenu::aboutToShow, this, &MainWindow::toolBarDockMenuAboutToShow);

    menuBar->addMenu(projectMenu);

    newProjectAction = projectMenu->addAction(QPixmap(":/images/newProject.png"), tr("New Project"), this, &MainWindow::newProject);
    openProjectAction = projectMenu->addAction(QPixmap(":/images/open.png"), tr("Open Project"), this, &MainWindow::openProjectMenuClicked);
    projectMenu->addSeparator();
    addExistingFileAction=projectMenu->addAction(tr("Add Existing File"), this, &MainWindow::addExistingFileToProject);
    addNewFileAction=projectMenu->addAction(tr("Add New File"), this, &MainWindow::addNewProjectFile);
    addNewClassAction=projectMenu->addAction(tr("Add New Class"), this, &MainWindow::addNewProjectClass);
    projectMenu->addSeparator();
    openExplorerAction=projectMenu->addAction(tr("Open Explorer"), this, &MainWindow::openExplorer);
    projectMenu->addSeparator();
    renameProjectAction=projectMenu->addAction(tr("Project Rename"), this, &MainWindow::renameProject);
    projectMenu->addSeparator();
    makefileAction=projectMenu->addAction(QPixmap(":/images/makefile.png"), tr("Open Makefile"), this, &MainWindow::openMakefile);
    projectMenu->addSeparator();
    clearTempFileAction = projectMenu->addAction(QPixmap(":/images/clear.png"), tr("Clear Temp Files"), this, &MainWindow::clearTempFileActionClicked);
    projectMenu->addSeparator();
    projectInformationAction = projectMenu->addAction(QPixmap(":/images/about.png"), tr("Project Information"), this, &MainWindow::projectInformation);
    projectMenu->addSeparator();
    closeProjectAction = projectMenu->addAction(QPixmap(":/images/closeProject.png"), tr("Close Project"), this, &MainWindow::closeProjectMenuClicked);

    connect(projectMenu, &QMenu::aboutToShow, this, &MainWindow::projectMenuAboutToShow);

    menuBar->addMenu(executeMenu);

    compileRunAction = executeMenu->addAction(QPixmap(":/images/compileRun.png"), tr("Compile && Run"), QKeySequence(Qt::CTRL|Qt::Key_R), this, &MainWindow::buildRun);
    compileAction = executeMenu->addAction(QPixmap(":/images/compile.png"), tr("Compile"), QKeySequence(Qt::CTRL|Qt::Key_B), this, &MainWindow::buildRun);
    runAction = executeMenu->addAction(QPixmap(":/images/run.png"), tr("Run"), QKeySequence("F2"), this, &MainWindow::runMenuClicked);
    executeMenu->addSeparator();
    buildDllAction=executeMenu->addAction(tr("Build DLL"), QKeySequence("F3"), this, &MainWindow::buildRun);
    buildLibAction=executeMenu->addAction(tr("Build LIB"), QKeySequence("F4"), this, &MainWindow::buildRun);
    executeMenu->addSeparator();
    debugAction=executeMenu->addAction(QPixmap(":/images/debug.png"), tr("GDB Debug"), QKeySequence("F5"), this, &MainWindow::debug);
    executeMenu->addSeparator();
    profileAction=executeMenu->addAction(QPixmap(":/images/profile.png"), tr("Performance Analysis"), QKeySequence("F6"), this, &MainWindow::permanceAnalysis);

    foreach (QAction *action, executeMenu->actions())
        action->setEnabled(false);

    menuBar->addMenu(toolsMenu);

    compilerOptionsAction=toolsMenu->addAction(QPixmap(":/images/compileOptions.png"), tr("Compiler Options"), this, &MainWindow::compilerOptions);
    toolsMenu->addSeparator();
    editorOptionsAction=toolsMenu->addAction(QPixmap(":/images/editorOptions.png"), tr("Editor Options"), this, &MainWindow::editorOptions);
    toolsMenu->addSeparator();
    fileManagerAction=toolsMenu->addAction(QPixmap(":/images/fileManager.png"), tr("File Manager"), this, &MainWindow::fileManager);
    consoleWindowAction=toolsMenu->addAction(QPixmap(":/images/cmd.png"), tr("Console Window"), this, &MainWindow::openCMD);
    toolsMenu->addSeparator();
    calculatorAction=toolsMenu->addAction(QPixmap(":/images/calculator.png"), tr("Calculator"), this, &MainWindow::openCalculator);
    toolsMenu->addSeparator();
    lineCountAction=toolsMenu->addAction(QPixmap(":/images/lineCount.png"), tr("Line Count"), this, &MainWindow::lineCount);

    menuBar->addMenu(aStyleMenu);

    formatFileAction = aStyleMenu->addAction(QPixmap(":/images/FormatFile.png"), tr("Format Source Code"), this, &MainWindow::formatFileActionClicked);
    formatFileAction->setEnabled(false);
    aStyleMenu->addSeparator();
    formatOptionsAction=aStyleMenu->addAction(QPixmap(":/images/FormattingOptions.png"), tr("Formatting Options"), this, &MainWindow::formatOptionActionClicked);

    menuBar->addMenu(windowMenu);

    fullscreenAction=windowMenu->addAction(tr("Fullscreen ON"), QKeySequence(Qt::CTRL|Qt::Key_F11), this, &MainWindow::fullscreenOnOff);
    windowMenu->addSeparator();
    closeAllTabAction=windowMenu->addAction(tr("Close All"), this, &MainWindow::closeAllTabs);

    menuBar->addMenu(helpMenu);

    manualAction=helpMenu->addAction(QPixmap(":/images/manual.png"), tr("Manual"), this, &MainWindow::manual);
    helpMenu->addSeparator();
    aboutAction=helpMenu->addAction(QPixmap(":/images/about.png"), tr("About..."), this, [ = ]()
            {
                QMessageBox::about(this, "About Cute C++", "<span style=font-weight:bold>Cute C++ v1.0 for C/C++ Programmers</span>"
                                            "<br><br><span style=white-space:pre>           Copyright(c) 2025 Cody-Lu"
                                            "<br><br>              GNU GPL v3.0 License"
                                            "<br><br>     https://gitee.com/codylu/cute-cpp</span>"
                                   );
            });

    mainToolBar = new QToolBar(this);	//工具栏控件
    mainTabWidget = new QTabWidget;
    infoTabWidget = new QTabWidget;
    outputInfoEdit = new QPlainTextEdit;
    infoTableView = new QTableView;
    infoItemModel = new QStandardItemModel();
    searchTableView = new QTableView;
    searchItemModel = new QStandardItemModel();
    vSplitter = new QSplitter(Qt::Vertical, this);
    hSplitter = new QSplitter(Qt::Horizontal, this);

    mainToolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea | Qt::LeftToolBarArea | Qt::RightToolBarArea);

    Qt::ToolBarArea toolBarArea = setting.value("ToolBarArea", Qt::TopToolBarArea).value<Qt::ToolBarArea>();
    if (toolBarArea == Qt::LeftToolBarArea)
    {
        this->addToolBar(Qt::LeftToolBarArea, mainToolBar); //添加toolBar工具栏到本窗口左边
        dockLeftAction->setIcon(yesPixmap);
    }
    else if (toolBarArea == Qt::RightToolBarArea)
    {
        this->addToolBar(Qt::RightToolBarArea, mainToolBar); //添加toolBar工具栏到本窗口右边
        dockRightAction->setIcon(yesPixmap);
    }
    else if (toolBarArea == Qt::BottomToolBarArea)
    {
        this->addToolBar(Qt::BottomToolBarArea, mainToolBar); //添加toolBar工具栏到本窗口底部
        dockBottomAction->setIcon(yesPixmap);
    }
    else
    {
        this->addToolBar(Qt::TopToolBarArea, mainToolBar); //其它情况，添加toolBar工具栏到本窗口上部
        dockTopAction->setIcon(yesPixmap);
    }

    QToolButton *newToolButton = new QToolButton;
    newToolButton->setDefaultAction(newAction);

    QToolButton *openToolButton = new QToolButton;
    openToolButton->setDefaultAction(openAction);

    QToolButton *saveToolButton = new QToolButton;
    saveToolButton->setDefaultAction(saveAction);

    QToolButton *saveAsToolButton = new QToolButton;
    saveAsToolButton->setDefaultAction(saveAsAction);

    QToolButton *undoToolButton = new QToolButton;
    undoToolButton->setDefaultAction(undoAction);

    QToolButton *redoToolButton = new QToolButton;
    redoToolButton->setDefaultAction(redoAction);

    QToolButton *cutToolButton = new QToolButton;
    cutToolButton->setDefaultAction(cutAction);

    QToolButton *copyToolButton = new QToolButton;
    copyToolButton->setDefaultAction(copyAction);

    QToolButton *pasteToolButton = new QToolButton;
    pasteToolButton->setDefaultAction(pasteAction);

    snippetToolButton = new QToolButton;
    snippetToolButton->setIcon(QPixmap(":/images/insertSnippet.png"));
    snippetToolButton->setPopupMode(QToolButton::InstantPopup);
    snippetToolButton->setMenu(snippetMenu);
    snippetToolButton->setToolTip(tr("Insert Snippet"));
    snippetToolButton->setEnabled(false);

    QToolButton *commentToolButton = new QToolButton;
    commentToolButton->setDefaultAction(commentOnOffAction);

    QToolButton *findToolButton = new QToolButton;
    findToolButton->setDefaultAction(findAction);

    QToolButton *compilerOptionsToolButton = new QToolButton;
    compilerOptionsToolButton->setDefaultAction(compilerOptionsAction);

    QToolButton *compileToolButton = new QToolButton;
    compileToolButton->setDefaultAction(compileAction);

    QToolButton *runToolButton = new QToolButton;
    runToolButton->setDefaultAction(runAction);

    QToolButton *compileRunToolButton = new QToolButton;
    compileRunToolButton->setDefaultAction(compileRunAction);

    mainToolBar->setIconSize(QSize(toolBarIconSize, toolBarIconSize));	//设置工具栏图标大小
    mainToolBar->setToolButtonStyle( Qt::ToolButtonIconOnly);
    mainToolBar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    mainToolBar->addWidget(newToolButton);
    mainToolBar->addWidget(openToolButton);
    mainToolBar->addWidget(saveToolButton);
    mainToolBar->addWidget(saveAsToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(undoToolButton);
    mainToolBar->addWidget(redoToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(cutToolButton);
    mainToolBar->addWidget(copyToolButton);
    mainToolBar->addWidget(pasteToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(commentToolButton);
    mainToolBar->addWidget(snippetToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(findToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(compilerOptionsToolButton);
    mainToolBar->addSeparator();
    mainToolBar->addWidget(compileToolButton);
    mainToolBar->addWidget(runToolButton);
    mainToolBar->addWidget(compileRunToolButton);

    mainTabWidget->setTabsClosable(true);
    mainTabWidget->setMovable(true);
    mainTabWidget->setAcceptDrops(false); //设置本窗口禁止接收Drop操作
    //mainTabWidget->setTabShape(QTabWidget::Triangular);
    mainTabWidget->setTabPosition(QTabWidget::North);
    connect(mainTabWidget, &QTabWidget::tabCloseRequested, this, &MainWindow::tabCloseRequested);
    connect(mainTabWidget, &QTabWidget::currentChanged, this, &MainWindow::currentTabChanged);

    infoTabWidget->setTabsClosable(false);
    infoTabWidget->setMovable(false);
    infoTabWidget->setAcceptDrops(false); //设置本窗口禁止接收Drop操作
    //infoTabWidget->setTabPosition(QTabWidget::South);

    outputInfoEdit->setReadOnly(true);
    outputInfoEdit->setFont(QFont(QStringList{"Consolas", "Microsoft Yahei"}, 11));

    //-----------------------设置infoTableView---------------------------------------------------
    QStringList infoHeaderList{tr("File        "), tr("   Line   "), tr("   Col   "), tr("   Type   "), tr("Message                ")};
    infoItemModel->setHorizontalHeaderLabels(infoHeaderList);

    infoTableView->setModel(infoItemModel);
    infoTableView->setFont(QFont(QStringList{"Consolas","Microsoft Yahei"},11));
    infoTableView->setAutoScroll(false);

    connect(infoTableView, &QTableView::doubleClicked, this, &MainWindow::tableViewDoubleClicked);

    infoTableView->setSelectionMode(QAbstractItemView::SingleSelection);
    infoTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
    infoTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);

    infoTableView->horizontalHeader()->setHighlightSections( false );
    infoTableView->setFrameShape(QFrame::NoFrame);

    infoTableView->setWordWrap(false);
    infoTableView->setAlternatingRowColors(true);
    infoTableView->setShowGrid(false); //不显示网格线
    infoTableView->setAlternatingRowColors(true); //设置相邻行颜色交替显示
    infoTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);//禁止单元格的编辑功能
    infoTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);

    infoTableView->setSelectionBehavior(QAbstractItemView::SelectRows);//点击选中整行，而非选中一个单元格
    infoTableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
    //infoTableView->setHorizontalHeaderLabels({"参数名","参数值"});

    infoTableView->verticalHeader()->setVisible(false); //垂直表头不显示
    infoTableView->verticalHeader()->setDefaultSectionSize(24);//设置所有列的默认行高
    infoTableView->verticalHeader()->setMinimumSectionSize(24);
    //infoTableView->setColumnWidth(0, 200); //设置第0列宽度

    //-----------------------设置searchTableView---------------------------------------------------
    QStringList searchHeaderList{tr("File"), tr("   Line"), tr("   Col"), tr("Code")};
    searchItemModel->setHorizontalHeaderLabels(searchHeaderList);

    SearchResultDelegate *delegate=new SearchResultDelegate();
    searchTableView->setItemDelegate(delegate);
    searchTableView->setModel(searchItemModel);
    searchTableView->setFont(QFont(QStringList{"Consolas","Microsoft Yahei"},11));
    searchTableView->setAutoScroll(false);

    connect(searchTableView, &QTableView::doubleClicked, this, &MainWindow::tableViewDoubleClicked);

    searchTableView->setSelectionMode(QAbstractItemView::SingleSelection);
    searchTableView->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
    searchTableView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);

    searchTableView->horizontalHeader()->setHighlightSections( false );
    searchTableView->setFrameShape(QFrame::NoFrame);
    searchTableView->setWordWrap(false);
    searchTableView->setAlternatingRowColors(true);
    searchTableView->setShowGrid(false); //不显示网格线
    searchTableView->setAlternatingRowColors(true); //设置相邻行颜色交替显示
    searchTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);//禁止单元格的编辑功能
    //searchTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);

    searchTableView->setSelectionBehavior(QAbstractItemView::SelectRows);//点击选中整行，而非选中一个单元格
    searchTableView->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
    //searchTableView->setHorizontalHeaderLabels({"参数名","参数值"});

    searchTableView->verticalHeader()->setVisible(false); //垂直表头不显示
    searchTableView->verticalHeader()->setDefaultSectionSize(24);//设置所有列的默认行高
    searchTableView->verticalHeader()->setMinimumSectionSize(24);
    searchTableView->setColumnWidth(0, 100); //设置第0列宽度
    searchTableView->setColumnWidth(1, 80); //设置第1列宽度
    searchTableView->setColumnWidth(2, 80); //设置第2列宽度
    searchTableView->setColumnWidth(3, 200); //设置第3列宽度

    //-------------------设置infoTabWidget--------------------
    infoTabWidget->addTab(outputInfoEdit, tr("Compile Log"));
    infoTabWidget->addTab(infoTableView, tr("Info List"));
    infoTabWidget->addTab(searchTableView, tr("Search Result"));
    infoTabWidget->addTab(new QWidget, QPixmap(":/images/upDown.png"), "");

    connect(infoTabWidget, &QTabWidget::currentChanged, this, &MainWindow::infoTabCurrentChanged);

    splitterSmallSizes << 5000 << 1000; //infoTabWidget高度稍小的情况
    splitterBigSizes << 2000 << 1000; //infoTabWidget高度稍大的情况

    treeWidget = new QTreeWidget();
    treeWidget->setStyle(QStyleFactory::create("windows"));
    //treeWidget->setFont(QFont(QStringList{"Consolas", "Microsoft Yahei"}, 11));
    //treeWidget->setStyleSheet("QTreeView::item { padding-bottom: 3px; }");//设置行间距
    //treeWidget->setHeaderHidden(true);
    QHeaderView *headerView = treeWidget->header();
    headerView->setFixedHeight(28);
    treeWidget->setHeaderLabel(tr("Project Manager"));
    treeWidget->setIconSize(QSize(20,20));

    renameFileAction=itemContextMenu->addAction(tr("Rename File"), this, &MainWindow::renameProjectFile);
    itemContextMenu->addSeparator();
    itemContextMenu->addAction(openExplorerAction);
    itemContextMenu->addSeparator();
    precompileAction=itemContextMenu->addAction(tr("Precompile ON"), this, &MainWindow::precompileOnOff);
    itemContextMenu->addSeparator();
    removeFileAction=itemContextMenu->addAction(QPixmap(":/images/removeFile.png"), tr("Remove file"), this, &MainWindow::removeProjectFile);

    connect(itemContextMenu, &QMenu::aboutToShow, this, &MainWindow::itemContextMenuAboutToShow);

    projectContextMenu->addAction(addExistingFileAction);
    projectContextMenu->addAction(addNewFileAction);
    projectContextMenu->addAction(addNewClassAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(openExplorerAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(renameProjectAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(makefileAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(clearTempFileAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(projectInformationAction);
    projectContextMenu->addSeparator();
    projectContextMenu->addAction(closeProjectAction);

    connect(projectContextMenu, &QMenu::aboutToShow, this, &MainWindow::projectContextMenuAboutToShow);

    connect(treeWidget, &QTreeWidget::itemDoubleClicked, this, &MainWindow::treeWidgetItemDoubleClicked);
    connect(treeWidget, &QTreeWidget::itemPressed, this, &MainWindow::treeWidgetItemPressed);

    hSplitter->addWidget(treeWidget);
    hSplitter->addWidget(vSplitter);

    QList<int> hSplitterSizes;
    hSplitterSizes << 1000 << 7000;

    QVariant hSplitterSizesVariant;
    hSplitterSizesVariant.setValue(hSplitterSizes);
    QList<int> hSizes = setting.value("hSplitterSizes", hSplitterSizesVariant).value<QList<int>>();
    hSplitter->setSizes(hSizes);
    //hSplitter->setStretchFactor(0, 1);//内部有子窗口的布局时，会失效
    //hSplitter->setStretchFactor(1, 8);//内部有子窗口的布局时，会失效

    vSplitter->addWidget(mainTabWidget); //将mainTabWidget添加到分割器splitter
    vSplitter->addWidget(infoTabWidget);//将infoTabWidget添加到分割器splitter

    infoTabWidget->setMinimumHeight(80);

    vSplitter->setCollapsible(0, false);
    vSplitter->setCollapsible(1, false);
    vSplitter->setHandleWidth(2);
    vSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    QVariant smallSizesVariant;
    smallSizesVariant.setValue(splitterSmallSizes);
    QList<int> sizes = setting.value("vSplitterSizes", smallSizesVariant).value<QList<int>>();
    vSplitter->setSizes(sizes);

    this->setCentralWidget(hSplitter);

    QStringList recentFiles;
    recentFiles = setting.value("RecentFiles").value<QStringList>();
    int count = recentFiles.size();
    for (int i = count - 1; i >= 0; i--)
        insertRecentFileAction(recentFiles[i]);

    statusBar = new QStatusBar; //定义主窗口底部显示的状态栏
    lineLabel = new QLabel; //定义一个标签用于添加到状态栏上显示信息
    columnLabel = new QLabel; //定义一个标签用于添加到状态栏上显示信息
    selectedLabel = new QLabel; //定义一个标签用于添加到状态栏上显示信息
    linesLabel = new QLabel; //定义一个标签用于添加到状态栏上显示信息
    compileModeLabel = new QLabel(compileMode); //定义一个标签用于添加到状态栏上显示信息
    encodingLabel = new QLabel; //定义一个标签用于添加到状态栏上显示信息

    this->setStatusBar(statusBar); //将statusBar设置为本窗口的状态栏
    label1=new QLabel(tr("  Line:"));
    statusBar->addWidget(label1);
    statusBar->addWidget(lineLabel);
    label2=new QLabel(tr("    Column:"));
    statusBar->addWidget(label2);
    statusBar->addWidget(columnLabel);
    label3=new QLabel(tr("    Selected:"));
    statusBar->addWidget(label3);
    statusBar->addWidget(selectedLabel);
    label4=new QLabel(tr("    Lines:"));
    statusBar->addWidget(label4);
    statusBar->addWidget(linesLabel);
    label5=new QLabel(tr("Compile Mode:"));
    statusBar->addPermanentWidget(label5);//添加encodingLabel到状态栏
    statusBar->addPermanentWidget(compileModeLabel);//添加encodingLabel到状态栏
    label6=new QLabel(tr("File Encoding:"));
    statusBar->addPermanentWidget(new QLabel("        "));//添加encodingLabel到状态栏
    statusBar->addPermanentWidget(label6);//添加encodingLabel到状态栏
    statusBar->addPermanentWidget(encodingLabel);//添加encodingLabel到状态栏

    if (encodingIndex==0)
        encodingLabel->setText("System");
    else
    {
        encodingLabel->setText("UTF-8");
        encodingIndex=1;
    }

    mainToolBar->setVisible(showToolBar);
    statusBar->setVisible(showStatusBar);

    server = new QLocalServer(this);
    //设置本地服务器，监听接收系统发来的媒体文件名，追加到播放列表中
    QObject::connect(server, &QLocalServer::newConnection, this, &MainWindow::newLocalConnection);
    if (!server->listen(QString("Cute_CPP")))
    {
        //监听失败，可能是程序崩溃时残留进程服务导致的，移除之。
        if (server->serverError() == QAbstractSocket::AddressInUseError)
        {
            QLocalServer::removeServer(server->serverName());
            server->listen(QString("Cute_CPP"));
        }
    }

    clipboardDataChanged();
    connect(clipboard, &QClipboard::dataChanged, this, &MainWindow::clipboardDataChanged);

    connect(&compilingTimer, &QTimer::timeout, this, &MainWindow::compilingProgress);

    if (GlobalVar::colorTheme == 0) changeToLightTheme();
    else if (GlobalVar::colorTheme == 1) changeToGrayTheme();
    else changeToDarkTheme();

    findReplaceDialog=new FindReplaceDialog(this, mainTabWidget);
    connect (findReplaceDialog->openedFilesFindReplaceButton, &QPushButton::clicked, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->openedFilesSearchLineEdit, &QLineEdit::returnPressed, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->openedFilesReplaceLineEdit, &QLineEdit::returnPressed, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->projectFindReplaceButton, &QPushButton::clicked, this, &MainWindow::projectOKButtonClicked);
    connect (findReplaceDialog->projectSearchLineEdit, &QLineEdit::returnPressed, this, &MainWindow::projectOKButtonClicked);
    connect (findReplaceDialog->projectReplaceLineEdit, &QLineEdit::returnPressed, this, &MainWindow::projectOKButtonClicked);

    connect(this, &MainWindow::sendBuildOutput, this, &MainWindow::buildOutputReadyRead);
}

void MainWindow::buildOutputReadyRead(QByteArray output)
{
    compilingTimer.stop();

    QString outputStr = output;
    if (!output.isValidUtf8())
        outputStr = cn(output);

    outputInfoEdit->appendPlainText(outputStr);

    compilingTimer.start(200);
}

void MainWindow::infoTabCurrentChanged(int index)
{
    if (index == 3)
    {
        infoTabWidget->setCurrentIndex(infoTabCurrentIndex);

        QList<int> sizes = vSplitter->sizes();
        double stretch = sizes.at(1) / (double)sizes.at(0);

        if (stretch <= 0.45)
        {
            splitterSmallSizes = sizes;
            vSplitter->setSizes(splitterBigSizes);
        }
        else
            vSplitter->setSizes(splitterSmallSizes);
    }
    else
    {
        infoTabCurrentIndex = infoTabWidget->currentIndex();
    }
}

void MainWindow::tableViewDoubleClicked(const QModelIndex & modelIndex)
{
    const QStandardItemModel *standardModel=(const QStandardItemModel*)(modelIndex.model());

    if (modelIndex.isValid())
    {
        int row = modelIndex.row();
        QStandardItem *item = standardModel->item(row, 0);
        QString rowString = standardModel->index(row, 1).data(Qt::DisplayRole).toString();
        QString colString = standardModel->index(row, 2).data(Qt::DisplayRole).toString();

        if (!rowString.isEmpty())
        {
            bool hasRowNum = false;
            int rowNum = rowString.toInt(&hasRowNum);
            if (hasRowNum)
            {
                QString fileName = item->toolTip().trimmed();
                bool hasColNum = false;
                int colNum = colString.toInt(&hasColNum);
                if (!hasColNum) colNum = 1;
                QString searchString=item->whatsThis();
                bool caseSensitive=item->data(Qt::UserRole).toBool();
                gotoLine(fileName, rowNum, colNum, searchString, caseSensitive);
            }
        }
    }
}

void MainWindow::clipboardDataChanged()
{
    //const QMimeData *mimeData = QApplication::clipboard()->mimeData();
    if (clipboard->text().isEmpty()) //if (mimeData->hasText())
        pasteAction->setEnabled(false);
    else
        pasteAction->setEnabled(true);
}

//接到新连接时，接收文件列表，追加到播放列表并播放第一个文件
void MainWindow::newLocalConnection()
{
    QLocalSocket *socket = server->nextPendingConnection();
    if (!socket) return;

    socket->waitForReadyRead(5000);   //等待读取，最多等5000毫秒，即5秒。

    QTextStream textStream(socket);
    //textStream.setEncoding(QStringConverter::Utf8);
    //textStream.setAutoDetectUnicode(true);
    //QString fileNamesString = textStream.codec()->fromUnicode(textStream.readAll());
    QString fileNamesString = textStream.readAll();
    delete socket;

    //如果主窗口没有显示，则激活主窗口进行显示。
    if (!this->hasFocus())
    {
        bool maximized = false;
        if (this->windowState()&Qt::WindowMaximized) maximized = true;
        this->setWindowState(Qt::WindowMinimized);
        if (maximized) this->showMaximized();
        else this->showNormal();
        this->activateWindow();
        this->setFocus();
    }

    if (fileNamesString == "NoParameter") return; //主程序启动时没有参数，直接返回。

    //获得fileNameList以后，追加到listWidget播放列表，然后播放。
    QStringList fileNameList = fileNamesString.split('\n', Qt::SkipEmptyParts);//用换行符分割字符串，移除空字符串
    //fileNameList.removeAll("");

    foreach (QString str, fileNameList)
        openSpecifiedFile(str);
}

//drag进来以后进行过滤和允许
void MainWindow::dragEnterEvent(QDragEnterEvent *e)
{
    e->acceptProposedAction(); //当文件被拖到本窗口上时，允许后续的操作。
}

//drag过来的文件drop到本窗口时，将文件打开显示。
void MainWindow::dropEvent(QDropEvent *e)
{
    QList<QUrl> urls = e->mimeData()->urls(); //从拖放事件对象中获得QUrl列表
    if (urls.isEmpty())
        return;

    //QString filename = urls.first().toLocalFile();
    foreach(QUrl url, urls)
        openSpecifiedFile(url.toLocalFile()); //将urls中的内容转换到字符串列表中
}

QString MainWindow::getUntitledName()
{
    int count = mainTabWidget->count();
    QStringList tabTextList;
    for (int i = 0; i < count; i++)
        tabTextList<<mainTabWidget->tabText(i);

    QString newFileName="Untitled";
    for (int i=1; i<1000; i++)
    {
        newFileName="Untitled"+QString::number(i);
        if (!tabTextList.contains(newFileName, Qt::CaseInsensitive))
            break;
    }

    return newFileName;
}

void MainWindow::newFile()
{
    addEditorTab(getUntitledName());
}

void MainWindow::openFile()
{
    QAction *clickedAction=dynamic_cast<QAction*>(sender());
    QString fileFilter="Project files (*.project)";
    if (clickedAction==openAction)
        fileFilter="Code files (*.c;*.cpp;*.cc;*.cxx;*.h;*.hxx;*.hpp;*.rc);;Project files (*.project);;C source files(*.c);;C++ source files(*.cpp;*.cc;*.cxx);;"
                    "Header files(*.h;*.hxx;*.hpp);;Resource files(*.rc);;Text file(*.txt);;All files(*.*)";

    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QString lastFilePath = setting.value("LastFilePath","").toString(); //获取上次的打开路径，如果获取不到，取缺省值为空字符串。
    if (lastFilePath.isEmpty()) lastFilePath=QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);//如果上次路径为空，则打开文档目录

    QStringList fileNames = QFileDialog::getOpenFileNames(this, "Open File or project", lastFilePath, fileFilter);
    foreach (QString fileName, fileNames)
        openSpecifiedFile(fileName);
}

void MainWindow::openProjectMenuClicked()
{
    QString fileFilter="Project files (*.project)";
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QString lastFilePath = setting.value("LastFilePath","").toString(); //获取上次的打开路径，如果获取不到，取缺省值为空字符串。
    if (lastFilePath.isEmpty()) lastFilePath=QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);//如果上次路径为空，则打开文档目录

    QString fileName = QFileDialog::getOpenFileName(this, "Open File or project", lastFilePath, fileFilter);

    if (!fileName.isEmpty())
        openProject(fileName);
}

void MainWindow::openSpecifiedFile(QString fileName)
{
    if (fileName.isEmpty())
        return;

    if (fileName.endsWith(".project", Qt::CaseInsensitive))
    {
        openProject(fileName);
        return;
    }

    //如果fileName已经打开，设为当前项，返回。
    int count = mainTabWidget->count();
    for (int i = 0; i < count; i++)
    {
        QWidget *widget = mainTabWidget->widget(i);
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        if (QFileInfo(editor->documentTitle()).absoluteFilePath() == QFileInfo(fileName).absoluteFilePath())
        {
            mainTabWidget->setCurrentIndex(i);
            return;
        }
    }

    bool success=false;
    QString finalStr=readStringFromFile(fileName, success);
    if (!success) return;

    //如果有Untitled1的空白Tab页，删除。
    if (mainTabWidget->count() == 1 && mainTabWidget->tabText(0) == "Untitled1")
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        if (!QFileInfo::exists(editor->documentTitle())&&!editor->document()->isUndoAvailable() && !editor->document()->isRedoAvailable() && editor->toPlainText() == "")
            mainTabWidget->removeTab(0);//如果Untitled1页没有更改过，则删除。
    }

    CodeEditor *editor = addEditorTab(fileName);
    editor->setPlainText(finalStr);//setPlainText执行后documentTitle会消失，所以下面需要重设。
    editor->setDocumentTitle(fileName);//appendPlainText没有这个问题，但undo会让所有代码消失。
    editor->document()->setModified(false);

    updateRecentFileActionList(fileName);

    //记录当前打开的路径作为下次打开时的路径
    QString filePath = QFileInfo(fileName).path();
    if (!filePath.isEmpty())
    {
        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);
        setting.setValue("LastFilePath", filePath); //记录当前路径到QSetting中保存
    }
}

QString MainWindow::readStringFromFile(QString fileName, bool &success)
{
    QFile file(fileName);
    success = file.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!success)
    {
        QMessageBox::warning(this, "Warning!", tr("%1Failed to read text from the above file!").arg(fileName+"\n\n"));
        return "";
    }

    QTextStream in(&file);
    in.setEncoding(QStringConverter::System);
    in.setAutoDetectUnicode(true);

    QString finalStr;
    QString testStr = in.read(1024); // 去读1k数据，用于测试编码格式
    if (in.encoding()!=QStringConverter::System)//如果是带bom的unicode文本文件。
    {
        in.seek(0);
        finalStr = in.readAll();//得到带BOM的unicode文本文件字符串
    }
    else //System，RTF-8或者其他格式编码的情况
    {
        file.reset();
        QByteArray tempByteArray=file.read(1024);
        auto toUtf8 = QStringDecoder(QStringDecoder::Utf8);
        auto toSystem = QStringDecoder(QStringDecoder::System);
        testStr=toUtf8(tempByteArray);
        testStr=toSystem(tempByteArray);

        if (toUtf8.hasError()&&toSystem.hasError())//非文本文件的情况
        {
            QMessageBox::warning(this, "Invalid text file!", tr("%1The above file is not a valid text file!").arg(fileName+"\n\n"));
            file.close();
            success=false;
            return "";
        }
        else//ANSI或者UTF8编码格式的情况
        {
            file.reset();
            QByteArray allByteArray = file.readAll();
            finalStr=byteArrayToString(allByteArray);
        }
    }

    file.close();

    return finalStr;
}

CodeEditor* MainWindow::addEditorTab(QString fileName)
{
    CodeEditor *editor = new CodeEditor(this);
    editor->setDocumentTitle(fileName);

    mainTabWidget->addTab(editor, QFileInfo(fileName).fileName());
    mainTabWidget->setCurrentWidget(editor);
    editor->setAcceptDrops(false); //设置本窗口不允许接收Drop操作
    editor->installEventFilter(this);

    connect(editor->document(), &QTextDocument::modificationChanged, this, &MainWindow::documentModificationChanged);
    connect(editor, &QPlainTextEdit::cursorPositionChanged, this, &MainWindow::documentCursorPositionChanged);
    connect(editor, &QPlainTextEdit::copyAvailable, this, &MainWindow::copyAvailableChanged);
    connect(editor, &QPlainTextEdit::undoAvailable, this, &MainWindow::undoAvailableChanged);
    connect(editor, &QPlainTextEdit::redoAvailable, this, &MainWindow::redoAvailableChanged);
    connect(editor, &QPlainTextEdit::selectionChanged, this, &MainWindow::selectionChanged);

    editor->document()->setModified(false);
    emit editor->cursorPositionChanged();
    editor->setFocus();

    return editor;
}

void MainWindow::undoAvailableChanged(bool yes)
{
    undoAction->setEnabled(yes);
}

void MainWindow::redoAvailableChanged(bool yes)
{
    redoAction->setEnabled(yes);
}

void MainWindow::copyAvailableChanged(bool yes)
{
    copyAction->setEnabled(yes);
    copyAsHtmlAction->setEnabled(yes);
    cutAction->setEnabled(yes);
    deleteAction->setEnabled(yes);
}

void MainWindow::selectionChanged()
{
    CodeEditor *editor = dynamic_cast<CodeEditor*>(sender());

    int selectionStart = editor->textCursor().selectionStart();
    int selectionEnd = editor->textCursor().selectionEnd();
    int textBlockNumberStart = editor->document()->findBlock(selectionStart).blockNumber();
    int textBlockNumberEnd = editor->document()->findBlock(selectionEnd).blockNumber();

    if (textBlockNumberStart != textBlockNumberEnd)
    {
        indentAction->setEnabled(true);
        unindentAction->setEnabled(true);
    }
    else
    {
        indentAction->setEnabled(false);
        unindentAction->setEnabled(false);
    }
}

void MainWindow::documentModificationChanged(bool changed)
{
    saveAction->setEnabled(changed);
}

void MainWindow::documentCursorPositionChanged()
{
    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    lineLabel->setText(QString::number(editor->textCursor().blockNumber() + 1));
    columnLabel->setText(QString::number(editor->textCursor().columnNumber() + 1));
    linesLabel->setText(QString::number(editor->document()->blockCount()));
    int selectionStart = editor->textCursor().selectionStart();
    int selectionEnd = editor->textCursor().selectionEnd();
    selectedLabel->setText(QString::number(selectionEnd - selectionStart));
}

void MainWindow::saveFileActionClicked()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
    saveFile(editor);
}

void MainWindow::saveAsFileActionClicked()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
    saveAsFile(editor);
}

void MainWindow::saveFile(CodeEditor *codeEditor)
{
    if (mainTabWidget->count() < 1) return;

    QString fileName = codeEditor->documentTitle();
    QFileInfo info(fileName);

    if (fileName.isEmpty() || !info.exists() || !info.isWritable())
    {
        saveAsFile(codeEditor);
    }
    else
    {
        bool savedSuccessfully = save(codeEditor->toPlainText(), fileName);
        if (savedSuccessfully)
        {
            codeEditor->document()->setModified(false);
        }
        else
        {
            QMessageBox::warning(this, "Error", tr("Failed to save file!"));
        }
    }
}

void MainWindow::saveAsFile(CodeEditor *editor)
{
    if (mainTabWidget->count() < 1) return;

    QString fileFullName=editor->documentTitle();
    QFileInfo fileInfo(editor->documentTitle());
    QString path=fileInfo.path();
    if (path=='.')
    {
        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

        QString lastFilePath = setting.value("LastFilePath","").toString(); //获取上次的打开路径，如果获取不到，取缺省值为空字符串。
        if (lastFilePath.isEmpty()) lastFilePath=QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);//如果上次路径为空，则打开文档目录
        fileFullName=lastFilePath+"/"+editor->documentTitle()+".cpp";
    }
    QString selectedFilter="All files(*.*)";
    if (fileInfo.suffix().compare("cpp", Qt::CaseInsensitive)==0)
        selectedFilter= "C++ source files(*.cpp)";
    else if (fileInfo.suffix().compare("c", Qt::CaseInsensitive)==0)
        selectedFilter= "C source files(*.c)";
    else if (fileInfo.suffix().compare("h", Qt::CaseInsensitive)==0)
        selectedFilter= "Header files(*.h)";
    else if (fileInfo.suffix().compare("hpp", Qt::CaseInsensitive)==0)
        selectedFilter= "Header files(*.hpp)";
    else if (fileInfo.suffix().compare("rc", Qt::CaseInsensitive)==0)
        selectedFilter= "Resource files(*.rc)";

    QString fileName = QFileDialog::getSaveFileName(this, "Save As", fileFullName, "C source files(*.c);;C++ source files(*.cpp);;"
                                    "Header files(*.h);;Header files(*.hpp);;Resource files(*.rc);;All files(*.*)", &selectedFilter);
    if (fileName.isEmpty())
        return;

    bool savedSuccessfully = save(editor->toPlainText(), fileName);
    if (savedSuccessfully)
    {
        editor->setDocumentTitle(fileName);
        editor->document()->setModified(false);
        mainTabWidget->setTabText(mainTabWidget->currentIndex(), QFileInfo(fileName).fileName());
        this->setWindowTitle("Cute C++ - " + fileName);

        //记录当前保存的路径作为下次打开时的路径
        QString filePath = QFileInfo(fileName).path();
        if (!filePath.isEmpty())
        {
            QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
            QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);
            setting.setValue("LastFilePath", filePath); //记录当前路径到QSetting中保存
        }

        updateRecentFileActionList(fileName);
    }
    else
    {
        QMessageBox::warning(this, "Error", tr("Failed to save file!"));
    }
}

bool MainWindow::save(QString sourceStr, QString fileName)
{
    //如果自动格式化开启，保存之前先启动AStyle进行代码格式化
    if (isAutoFormat)
        formatFileActionClicked();

    //打开文件进行保存，成功返回true，失败返回false。
    QFile file(fileName); //file.write(str.toUtf8()); //这种形式更简洁。
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);

        QStringConverter::Encoding encoding = QStringConverter::System;
        if (encodingIndex != 0)
            encoding = QStringConverter::Utf8;

        out.setEncoding(encoding);

        out << sourceStr;
        file.close();
        return true;
    }
    else
    {
        return false;
    }
}

void MainWindow::updateRecentFileActionList(QString fileName)
{
    //删掉重名的最近打开的菜单项
    QList<QAction *> actions = historyMenu->actions();
    foreach (QAction *action, actions)
    {
        if (action->text() == fileName)
            historyMenu->removeAction(action);
    }
}

void MainWindow::exportFile()
{
    if (mainTabWidget->count() < 1) return;

    QAction *action = dynamic_cast<QAction*>(sender());

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor*>(currentWidget);

    CodeEditor *editorClone = new CodeEditor;
    editorClone->setFont(editor->font());
    editorClone->setTabStopDistance(editor->tabStopDistance());

    QTextCursor textCursorClone = editorClone->textCursor();
    QString text = editor->toPlainText();
    editorClone->setPlainText(text);

    QTextBlock currentBlock = editor->document()->begin();
    while (currentBlock.isValid())
    {
        foreach (QTextLayout::FormatRange r, currentBlock.layout()->formats())
        {
            textCursorClone.setPosition(currentBlock.position() + r.start, QTextCursor::MoveAnchor);
            textCursorClone.setPosition(currentBlock.position() + r.start + r.length, QTextCursor::KeepAnchor);
            textCursorClone.mergeCharFormat(r.format);
        }
        currentBlock = currentBlock.next();
    }

    QFileInfo info(editor->documentTitle());

    QString sourceName = mainTabWidget->tabText(mainTabWidget->currentIndex());

    QString exportName = QFileInfo(sourceName).completeBaseName() + ".doc";
    if (action == exportHTMLAction)
        exportName = QFileInfo(sourceName).completeBaseName() + ".html";

    exportName=info.absolutePath()+"/"+exportName;

    QString fileName;

    if (action == exportDocAction)
        fileName = QFileDialog::getSaveFileName(this, "Export to Word", exportName, "Word files(*.doc)");
    else
        fileName = QFileDialog::getSaveFileName(this, "Export to Html", exportName, "HTML files(*.html)");

    if (fileName.isEmpty()) return;

    QTextDocumentWriter writer(fileName);

    if (action == exportDocAction) //导出到word文件时用ODF格式，Open Document Format，基于XML的一种公开文档格式
        writer.setFormat("ODF");
    else
        writer.setFormat("HTML");

    bool success = writer.write(editorClone->document());
    if (!success)
        QMessageBox::warning(this, "Export to Word/Html", tr("Failed in exporting to file!"));

    delete editorClone;
}

void MainWindow::print()
{
    QWidget *currentWidget = mainTabWidget->currentWidget();
    if (!currentWidget) return;

    QPrinter printer(QPrinter::HighResolution);
    //if (editor->textCursor().hasSelection())
    //printer.setPrintRange(QPrinter::Selection);

    printer.setPageOrientation(QPageLayout::Portrait);//Portrait, Landscape
    printer.setPageSize(QPageSize(QPageSize::A4));
    //printer.setFullPage(true);
    printer.setPageMargins(QMarginsF(0.0f, 0.0f, 0.0f, 0.0f), QPageLayout::Point);
    printer.setOutputFormat(QPrinter::NativeFormat);

    QPrintPreviewDialog preview(&printer, this);// 创建打印预览对话框
    preview.setWindowFlags(Qt::Window); //设置本对话框的右上方最小化最大化关闭按钮和Window一样
    preview.setMinimumSize(960, 600);
    //paintRequested(QPrinter *printer)是系统提供的，当preview.exec()执行时该信号被触发，
    connect(&preview, &QPrintPreviewDialog::paintRequested, this, &MainWindow::printText);

    preview.exec();
}

void MainWindow::printText(QPrinter *printer)//printText(QPrinter *printer)是自定义的槽函数，图像的绘制就在这个函数里。
{
    try
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        if (!currentWidget) return;

        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        editor->setWordWrapMode(QTextOption::WordWrap);
        editor->print(printer);
        editor->setWordWrapMode(QTextOption::NoWrap);
    }
    catch(...)
    {
        QMessageBox::warning(this, "Warning!", tr("Something wrong in print preview!"));
    }
}

void MainWindow::find()
{
    if (mainTabWidget->count() <= 0)
        return;

    //根据项目是否存在，确定在项目中检索功能是否激活
    if (treeWidget->topLevelItemCount()==0)
        findReplaceDialog->searchDialogTabWidget->widget(2)->setEnabled(false);
    else
        findReplaceDialog->searchDialogTabWidget->widget(2)->setEnabled(true);

    findReplaceDialog->setReplaceOn(false);
    findReplaceDialog->show();
}

void MainWindow::replace()
{
    if (mainTabWidget->count() <= 0)
        return;

    //根据项目是否存在，确定在项目中检索功能是否激活
    if (treeWidget->topLevelItemCount()==0)
        findReplaceDialog->searchDialogTabWidget->widget(2)->setEnabled(false);
    else
        findReplaceDialog->searchDialogTabWidget->widget(2)->setEnabled(true);

    findReplaceDialog->setReplaceOn(true);
    findReplaceDialog->show();
}

void MainWindow::findPreviousNext()
{
    if (mainTabWidget->count() <= 0)
        return;

    QAction* action = dynamic_cast<QAction*>(sender());

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        QString findString = findReplaceDialog->findComboBox->currentText();
        findString = findString.trimmed();
        if (findString.isEmpty())
        {
            QMessageBox::information(this, "Find && Replace", tr("Please input a string to search!"));
            return;
        }

        QTextDocument::FindFlags options;
        if (action == findPreviousAction)
            options = QTextDocument::FindBackward;
        if (findReplaceDialog->caseCheckBox->isChecked())
            options = options | QTextDocument::FindCaseSensitively;
        if (findReplaceDialog->wholeWordsCheckBox->isChecked())
            options = options | QTextDocument::FindWholeWords;

        bool found = editor->find(findString, options);
        if (!found)
        {
            QApplication::beep();
            QMessageBox::information(this, "Information", "\""+findString + "\""+tr(" not found!"));
        }
    }
}

void MainWindow::gotoLineActionClicked()
{
    if (mainTabWidget->count() <= 0) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    int currentLineNo = editor->textCursor().blockNumber() + 1;
    int minValue = 1;
    int maxValue = editor->document()->blockCount();
    int stepValue = 1;
    QString dlgTitle = "Go to line...";
    QString txtLabel = QString("New Line (1~%1): ").arg(maxValue);
    bool OK = false; //QInputDialog的确定按钮被点击，返回true，否则false;
    int newLineNo = QInputDialog::getInt(this, dlgTitle, txtLabel, currentLineNo, minValue, maxValue, stepValue, &OK);
    if (OK)
    {
        gotoLine(editor->documentTitle(), newLineNo, 1, "", false);
    }
}

void MainWindow::gotoLine(QString fileName, int rowNum, int colNum, QString searchString, bool caseSensitive)
{
    CodeEditor *targetEditor = nullptr;

    int tabCount = mainTabWidget->count();
    for (int i = 0; i < tabCount; i++)
    {
        QWidget * widget = mainTabWidget->widget(i);
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        if (QFileInfo(editor->documentTitle()).absoluteFilePath() == QFileInfo(fileName).absoluteFilePath())
        {
            targetEditor = editor;
            break;
        }
    }

    if (targetEditor == nullptr)
    {
        QFileInfo fileInfo(fileName);
        if (fileInfo.exists())
        {
            openSpecifiedFile(fileName);

            QWidget *widget = mainTabWidget->currentWidget();
            if (widget)
            {
                CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
                if (QFileInfo(editor->documentTitle()).absoluteFilePath() == QFileInfo(fileName).absoluteFilePath())
                    targetEditor = editor;
            }
        }
        else
        {
            QMessageBox::warning(this, "Goto line...", fileName + "\n\n"+tr("The above file does not exist!"));
            return;
        }
    }

    if (targetEditor)
    {
        targetEditor->setCenterOnScroll(true);

        mainTabWidget->setCurrentWidget(targetEditor);
        int maxRowNum = targetEditor->document()->blockCount();
        if (rowNum > maxRowNum) rowNum = maxRowNum;
        if (rowNum < 1) rowNum = 1;

        QTextBlock textBlock = targetEditor->document()->findBlockByLineNumber(rowNum - 1);
        QTextCursor textCursor = targetEditor->textCursor();

        targetEditor->setShowErrorLine(true);
        int cursorPos = textBlock.position() + colNum - 1;
        if (colNum - 1 > textBlock.length() - 1)
            cursorPos = textBlock.position() + textBlock.length() - 1;

        int previousPos = textCursor.position();
        textCursor.setPosition(cursorPos);

        if (previousPos == cursorPos)//在状态栏显示光标新位置
            emit targetEditor->cursorPositionChanged();

        if (!searchString.isEmpty())
        {
            QRegularExpression re(searchString, caseSensitive?QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
            QString lineString=textBlock.text();
            QRegularExpressionMatch match=re.match(lineString,colNum-1);//从指定列开始查找。
            if (match.hasMatch())
            {
                textCursor.setPosition(textBlock.position()+match.capturedStart() + match.capturedLength(), QTextCursor::MoveAnchor);
                textCursor.setPosition(textBlock.position()+match.capturedStart(), QTextCursor::KeepAnchor);
            }
            else
            {
                match=re.match(lineString,0);//如果没有找到，从头开始再查找一次。
                if (match.hasMatch())
                {
                    textCursor.setPosition(textBlock.position()+match.capturedStart() + match.capturedLength(), QTextCursor::MoveAnchor);
                    textCursor.setPosition(textBlock.position()+match.capturedStart(), QTextCursor::KeepAnchor);
                }
                else
                {
                    targetEditor->setFocus();
                    targetEditor->setTextCursor(textCursor);
                    QMessageBox::information(this, tr("No match"), tr("No match string in line %1 anymore, please search again!").arg(rowNum));
                }
            }
        }

        targetEditor->setFocus();
        targetEditor->setTextCursor(textCursor);

        targetEditor->setCenterOnScroll(false);
    }
    else
    {
        QMessageBox::warning(this, "Goto line...", fileName + "\n\n"+tr("The above file is invalid!"));
    }
}

void MainWindow::compilerOptions()
{
    CompileOptionDialog compileOptionDialog(this);//显示编译器选项对话框

    //检测项目是否处于激活状态
    bool projectIsActive=false;

    if (mainTabWidget->count() ==0&&treeWidget->topLevelItemCount()>0)
        projectIsActive=true;//设置项目为激活状态

    if (mainTabWidget->count() >0&&treeWidget->topLevelItemCount()>0)
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        QString fileName = editor->documentTitle();
        QFileInfo fileInfo(fileName);
        //检测是否是单个文件还是项目文件
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        int childCount=topItem->childCount();
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *subItem=topItem->child(i);
            QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
            if (subInfo.absoluteFilePath()==fileInfo.absoluteFilePath())
            {
                projectIsActive=true;//设置项目为激活状态，退出循环
                break;
            }
        }
    }

    if (projectIsActive)//如果项目处于激活状态
    {
        //读取编译器列表信息和前次使用的编译器索引号
        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

        QList<CompilerSet> compilerSetList;
        compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
        int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

        CompilerSet compilerSet;
        if (compilerSetList.count() > 0)
        {
            if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
                compilerSet = compilerSetList[compilerIndex];
            else
                compilerSet = compilerSetList[0];
        }

        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        CompilerSet projectCompilerSet=topItem->data(0, Qt::WhatsThisRole).value<CompilerSet>();
        //将项目的编译链接参数复制给当前编译器，但不包括modeIndex
        compilerSet.compileArgCheckBoxState=projectCompilerSet.compileArgCheckBoxState;
        compilerSet.linkArgCheckBoxState=projectCompilerSet.linkArgCheckBoxState;
        compilerSet.warningAllCheckBoxState=projectCompilerSet.warningAllCheckBoxState;
        compilerSet.warningExtraCheckBoxState=projectCompilerSet.warningExtraCheckBoxState;
        compilerSet.staticLinkCheckBoxState=projectCompilerSet.staticLinkCheckBoxState;
        compilerSet.compileArg=projectCompilerSet.compileArg;
        compilerSet.linkArg=projectCompilerSet.linkArg;
        //用项目的编译链接参数修改过的compilerSet重新设定对话框的编译链接参数
        compileOptionDialog.loadDataFromCompilerSet(compilerSet);
    }

    if (compileOptionDialog.exec()==QDialog::Accepted)
    {
        compileModeLabel->setText(compileOptionDialog.getCompileMode());

        if (projectIsActive)
        {
            //读取编译器列表信息和前次使用的编译器索引号
            QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
            QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

            QList<CompilerSet> compilerSetList;
            compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
            int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

            CompilerSet compilerSet;
            if (compilerSetList.count() > 0)
            {
                if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
                    compilerSet = compilerSetList[compilerIndex];
                else
                    compilerSet = compilerSetList[0];
            }

            QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
            CompilerSet projectCompilerSet=topItem->data(0, Qt::WhatsThisRole).value<CompilerSet>();
            //如果当前编译器参数和项目的编译器参数不同，则删除项目的临时文件，并将当前编译器复制给项目的编译器
            if (notEqualCompilerSet(projectCompilerSet, compilerSet))
            {
                clearTempFiles(QStringList{"*.o", "*.res", "*.gch"});
                QVariant variant;
                variant.setValue(compilerSet);
                topItem->setData(0, Qt::WhatsThisRole, variant);
            }
        }
    }
}

void MainWindow::editorOptions()
{
    EditorOptionDialog editorOptionDialog(this);

    if (editorOptionDialog.exec() != QDialog::Accepted) return;

    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    //如果新编码索引号和原先的不同，则更改为新编码格式索引。
    int selectedEncodingIndex = setting.value("EncodingIndex", 0).toInt();
    if (encodingIndex != selectedEncodingIndex)
        encodingIndex = selectedEncodingIndex;

    if (encodingIndex==0)
        encodingLabel->setText("System");
    else
    {
        encodingLabel->setText("UTF-8");
        encodingIndex=1;
    }

    //对打开的文件设置新字体和新Tab宽度
    for (int i = 0; i < mainTabWidget->count(); i++)
    {
        QWidget *widget = mainTabWidget->widget(i);

        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        editor->setTabSpaceNumber(setting.value("EditorTabWidth",4).toInt());

        QFont font = setting.value("EditorFont", QFont(QStringList{"Consolas"}, 12)).value<QFont>();
        font.setFamilies(QStringList{font.family(), "Microsoft Yahei"});
        font.setPointSize(setting.value("EditorFontSize", 12).toInt());
        editor->setFont(font);

        QFontMetrics fm(editor->font());
        double fontWidth = fm.horizontalAdvance("a");
        editor->setTabStopDistance(fontWidth * editor->getTabSpaceNumber());
    }

    //如果选择的语言和现在显示的不同，更改编辑器菜单的显示语言，并设置language为新选择语言
    QString newLanguage = setting.value("Language", "Chinese").toString();
    if (language != newLanguage)
    {
        changeMenuLanguage(newLanguage);
        language = newLanguage;
    }

    //如果工具栏图标尺寸有更新，则重新设置工具栏尺寸。
    int newIconSize = setting.value("IconSize", 28).toInt();
    if (newIconSize != toolBarIconSize)
    {
        QList<QAction*> toolButtonActions = mainToolBar->actions();
        mainToolBar->clear();
        mainToolBar->setIconSize(QSize(newIconSize, newIconSize));
        mainToolBar->addActions(toolButtonActions);
        toolBarIconSize = newIconSize;
    }
}

void MainWindow::colorThemeOptions()
{
    ColorThemeDialog colorThemeDialog;

    if (colorThemeDialog.exec()==QDialog::Accepted)
    {
        int count = mainTabWidget->count();
        for (int i = 0; i < count; i++)
        {
            QWidget *widget = mainTabWidget->widget(i);
            CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
            editor->changeColorTheme();
        }

        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

        QVariant variant;
        variant.setValue(GlobalVar::keywordColor);
        setting.setValue("keywordColor", variant);

        variant.setValue(GlobalVar::functionColor);
        setting.setValue("functionColor", variant);

        variant.setValue(GlobalVar::numberColor);
        setting.setValue("numberColor", variant);

        variant.setValue(GlobalVar::qtClassColor);
        setting.setValue("qtClassColor", variant);

        variant.setValue(GlobalVar::preprocessColor);
        setting.setValue("preprocessColor", variant);

        variant.setValue(GlobalVar::commentColor);
        setting.setValue("commentColor", variant);

        variant.setValue(GlobalVar::stringColor);
        setting.setValue("stringColor", variant);

        variant.setValue(GlobalVar::symbolColor);
        setting.setValue("symbolColor", variant);

        variant.setValue(GlobalVar::editorForeground);
        setting.setValue("editorForeground", variant);
    }
}

void MainWindow::changeMenuLanguage(QString language)
{
    if (language == "Chinese")
    {
        qApp->removeTranslator(GlobalVar::translator);
        delete GlobalVar::translator;
        GlobalVar::translator=new QTranslator;
        if (GlobalVar::translator->load(":Cute-CPP_zh_CN.qm"))
            qApp->installTranslator(GlobalVar::translator);
    }
    else
    {
        qApp->removeTranslator(GlobalVar::translator);
    }

    fileMenu->setTitle(tr("&File"));
    newAction->setText(tr("New File"));
    openAction->setText(tr("Open File"));
    saveAction->setText(tr("Save"));
    saveAsAction->setText(tr("Save As"));
    printAction->setText(tr("Print"));
    exportMenu->setTitle(tr("Export"));
    historyMenu->setTitle(tr("History List"));
    exportDocAction->setText(tr("to Word"));
    exportHTMLAction->setText(tr("to HTML"));
    clearRecentAction->setText(tr("Clear History"));
    exitAction->setText(tr("Exit"));

    editMenu->setTitle(tr("&Edit"));
    undoAction->setText(tr("Undo"));
    redoAction->setText(tr("Redo"));
    cutAction->setText(tr("Cut"));
    copyAction->setText(tr("Copy"));
    pasteAction->setText(tr("Paste"));
    selectAllAction->setText(tr("Select All"));
    deleteAction->setText(tr("Delete Selection"));
    copyAsHtmlAction->setText(tr("Copy As Html"));
    snippetMenu->setTitle(tr("Insert Snippet"));
    indentAction->setText(tr("Indent"));
    unindentAction->setText(tr("Unindent"));
    commentOnOffAction->setText(tr("Toggle Comment"));

    searchMenu->setTitle(tr("&Search"));
    findAction->setText(tr("Find"));
    replaceAction->setText(tr("Replace"));
    findPreviousAction->setText(tr("Find Previous"));
    findNextAction->setText(tr("Find Next"));
    gotoLineAction->setText(tr("Goto Line"));

    viewMenu->setTitle(tr("&View"));
    zoomInAction->setText(tr("Zoom In"));
    zoomOutAction->setText(tr("Zoom Out"));
    toolBarAction->setText(tr("ToolBar"));
    statusBarAction->setText(tr("StatusBar"));

    toolBarDockMenu->setTitle(tr("ToolBar Dock"));
    dockTopAction->setText(tr("Top"));
    dockBottomAction->setText(tr("Bottom"));
    dockLeftAction->setText(tr("Left"));
    dockRightAction->setText(tr("Right"));

    colorThemeMenu->setTitle(tr("Select Color Theme"));
    lightThemeAction->setText(tr("Light"));
    grayThemeAction->setText(tr("Gray"));
    darkThemeAction->setText(tr("Dark"));
    colorThemeOptionAction->setText(tr("Syntax Highlight Customize"));

    projectMenu->setTitle(tr("&Project"));
    newProjectAction->setText(tr("New Project"));
    openProjectAction->setText(tr("Open Project"));
    clearTempFileAction->setText(tr("Clear Temp Files"));
    makefileAction->setText(tr("Open Makefile"));
    closeProjectAction->setText(tr("Close Project"));
    projectInformationAction->setText(tr("Project Information"));

    executeMenu->setTitle(tr("E&xecute"));
    compileAction->setText(tr("Compile"));
    runAction->setText(tr("Run"));
    compileRunAction->setText(tr("Compile && Run"));
    buildDllAction->setText(tr("Build DLL"));
    buildLibAction->setText(tr("Build LIB"));
    debugAction->setText(tr("GDB Debug"));
    profileAction->setText(tr("Performance Analysis"));

    toolsMenu->setTitle(tr("&Tools"));
    compilerOptionsAction->setText(tr("Compiler Options"));
    editorOptionsAction->setText(tr("Editor Options"));
    fileManagerAction->setText(tr("File Manager"));
    consoleWindowAction->setText(tr("Console Window"));
    calculatorAction->setText(tr("Calculator"));
    lineCountAction->setText(tr("Line Count"));

    formatFileAction->setText(tr("Format Source Code"));
    formatOptionsAction->setText(tr("Formatting Options"));

    windowMenu->setTitle(tr("&Window"));

    if ((this->windowState()&Qt::WindowFullScreen)==Qt::WindowFullScreen)
        fullscreenAction->setText(tr("Fullscreen OFF"));
    else
        fullscreenAction->setText(tr("Fullscreen ON"));

    closeAllTabAction->setText(tr("Close All"));

    helpMenu->setTitle(tr("&Help"));
    manualAction->setText(tr("Manual"));
    aboutAction->setText(tr("About..."));

    renameFileAction->setText(tr("Rename File"));
    removeFileAction->setText(tr("Remove File"));
    openExplorerAction->setText(tr("Open Explorer"));

    addExistingFileAction->setText(tr("Add Existing File"));
    addNewFileAction->setText(tr("Add New File"));
    addNewClassAction->setText(tr("Add New Class"));
    renameProjectAction->setText(tr("Project Rename"));

    snippetToolButton->setToolTip(tr("Insert Snippet"));

    if (findReplaceDialog != nullptr)
        delete findReplaceDialog;

    findReplaceDialog = new FindReplaceDialog(this, mainTabWidget);
    connect (findReplaceDialog->openedFilesFindReplaceButton, &QPushButton::clicked, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->openedFilesSearchLineEdit, &QLineEdit::returnPressed, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->openedFilesReplaceLineEdit, &QLineEdit::returnPressed, this, &MainWindow::openedFilesOKButtonClicked);
    connect (findReplaceDialog->projectFindReplaceButton, &QPushButton::clicked, this, &MainWindow::projectOKButtonClicked);
    connect (findReplaceDialog->projectSearchLineEdit, &QLineEdit::returnPressed, this, &MainWindow::projectOKButtonClicked);
    connect (findReplaceDialog->projectReplaceLineEdit, &QLineEdit::returnPressed, this, &MainWindow::projectOKButtonClicked);

    infoTabWidget->setTabText(0, tr("Compile Log"));
    infoTabWidget->setTabText(1, tr("Info List"));
    infoTabWidget->setTabText(2, tr("Search Result"));

    QStringList infoHeaderList{tr("File        "), tr("   Line   "), tr("   Col   "), tr("   Type   "), tr("Message                ")};
    infoItemModel->setHorizontalHeaderLabels(infoHeaderList);
    QStringList searchHeaderList{tr("File"), tr("   Line"), tr("   Col"), tr("Code")};
    searchItemModel->setHorizontalHeaderLabels(searchHeaderList);

    treeWidget->setHeaderLabel(tr("Project Manager"));

    label1->setText(tr("  Line:"));
    label2->setText(tr("    Column:"));
    label3->setText(tr("    Selected:"));
    label4->setText(tr("    Lines:"));
    label5->setText(tr("Compile Mode:"));
    label6->setText(tr("File Encoding:"));
}

//启动文件管理器，并选中当前文件
void MainWindow::fileManager()
{
    QProcess *process = new QProcess(this);
    if (mainTabWidget->count() < 1)
    {
        process->start("explorer.exe");
    }
    else
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        QFileInfo info(editor->documentTitle());
        if (!info.exists())
        {
            process->start("explorer.exe");
        }
        else
        {
            QString path = editor->documentTitle();
            QStringList args;
            args << "/select," << QDir::toNativeSeparators(path);
            process->start("explorer.exe", args);
        }
    }
}

//打开控制台窗口console window
void MainWindow::openCMD()
{
    QProcess *process = new QProcess(this);
    QStringList arguments;

    if (mainTabWidget->count() >= 1)
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        QFileInfo info(editor->documentTitle());
        if (info.exists())
            //arguments = QStringList() << "/k" << "cd /d" + info.path() +"/ && "+info.completeBaseName();
            arguments = QStringList() << "/k" << "cd /d" + info.path();
    }

    QString program = "cmd.exe";
    //arguments = QStringList() << "/K" << "C:\\Program Files\\Inkscape\\python.exe";
    process->setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments * args)
                                               {
                                                   args->flags |= CREATE_NEW_CONSOLE;
                                                   args->startupInfo->dwFlags &= ~ STARTF_USESTDHANDLES;
                                               });
    process->start(program, arguments);

    //process->write("cd /d C:/Users/www/Desktop/BG/ && start Test.exe\n");
    process->waitForStarted();
}

void MainWindow::openCalculator()
{
    QProcess *process = new QProcess(this);
    QString program = "calc.exe";

    process->start(program);
    bool started = process->waitForStarted();
    if (!started)
    {
        QMessageBox::warning(this, "Start Calculator", tr("Calculator program (calc.exe) does not exist!"));
        return;
    }
}

void MainWindow::lineCount()
{
    int totalLineCount=0;
    int count = mainTabWidget->count();

    for (int i = 0; i < count; i++)
    {
        QWidget *widget = mainTabWidget->widget(i);
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);

        totalLineCount+=(editor->document()->lineCount());
    }

    QMessageBox::information(this, "Line Count", QString(tr("There are %1 opened files, the total number of lines is %2.")).arg(count).arg(totalLineCount));
}

void MainWindow::openMakefile()
{
    int topLevelCount=treeWidget->topLevelItemCount();
    if (topLevelCount==0) return;

    QTreeWidgetItem *item=treeWidget->topLevelItem(0);
    QString projectFile=item->data(0, Qt::UserRole).toString();
    QString makefile=QFileInfo(projectFile).path()+"/Makefile.mk";

    if (!QFileInfo::exists(makefile))
    {
        QMessageBox::information(this, "No makefile", tr("There is no makefile generated yet!"));
        return;
    }

    QFile file(makefile);
    bool success = file.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!success)
    {
        QMessageBox::warning(this, "Warning!", tr("%1Failed to read text from the above file!").arg(makefile+"\n\n"));
        return;
    }

    QTextStream in(&file);
    in.setEncoding(QStringConverter::System);
    QString makeFileString = in.readAll();
    file.close();

    CodeEditor *textEdit=new CodeEditor;
    textEdit->setWindowTitle(projectFile+" - Makefile.mk");
    textEdit->setWindowIcon(QPixmap(":/images/cute-cpp.png"));
    textEdit->resize(960,720);
    textEdit->setPlainText(makeFileString);
    textEdit->show();
}

void MainWindow::debug()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    QString gdbFile = "gdb.exe";

    QString fileName = editor->documentTitle();
    QFileInfo info(fileName);

    QString executableName = QDir::cleanPath(info.path()) + QDir::separator() + info.completeBaseName() + ".exe";

    //检测是否是单个文件还是项目文件
    if (treeWidget->topLevelItemCount()!=0)
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        QFileInfo projectFileInfo=QFileInfo(topItem->data(0, Qt::UserRole).toString());
        int childCount=topItem->childCount();
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *subItem=topItem->child(i);
            QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
            if (subInfo.absoluteFilePath()==info.absoluteFilePath())
            {
                executableName=projectFileInfo.path()+"/"+projectFileInfo.completeBaseName()+".exe";
                break;
            }
        }
    }

    //如果没有可执行文件，输出信息并退出。
    if (!QFileInfo::exists(executableName))
    {
        QMessageBox::warning(this, "Warning", tr("No executable file for debugging!"));
        return;
    }

    if (QMessageBox::No == QMessageBox::information(this, "Information",
                                                    tr("The source file should be compiled in Debug Mode,"
                                                       "so the executable file contains debug information. "
                                                       "After lanuching GDB, set breakpoint by yourself to debug your program")+
                                                    "\n\n"+tr("Are you sure to continue debugging?"),
                                                    QMessageBox::Yes | QMessageBox::No))
    {
        return;
    }

    QProcess *process = new QProcess(this);
    process->setWorkingDirectory(QFileInfo(executableName).path());
    addPathsToEnv();
    process->setCreateProcessArgumentsModifier([](QProcess::CreateProcessArguments * args)
                                               {
                                                   args->flags |= CREATE_NEW_CONSOLE;
                                                   args->startupInfo->dwFlags &= ~ STARTF_USESTDHANDLES;
                                               });

    QStringList arguments;
    QString exeName=QFileInfo(executableName).fileName();
    arguments << "\""+exeName+"\"";

    process->start(gdbFile, arguments);
    bool started = process->waitForStarted();

    outputInfoEdit->setPlainText("");
    infoItemModel->removeRows(0, infoItemModel->rowCount());
    infoTableView->repaint();

    if (!started)
    {
        outputInfoEdit->setPlainText("GDB debugger is invalid!");
        QMessageBox::warning(this, "GDB Debug", tr("GDB debugger is invalid!"));
        return;
    }
}

void MainWindow::permanceAnalysis()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    QString gprofFile = "gprof.exe";

    //检查文件是否已经保存
    QString fileName = editor->documentTitle();
    QFileInfo info(fileName);

    QString executableName = info.path() + "/" + info.completeBaseName() + ".exe";

    //检测是否是单个文件还是项目文件
    if (treeWidget->topLevelItemCount()>0)
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        int childCount=topItem->childCount();
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *subItem=topItem->child(i);
            QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
            if (subInfo.absoluteFilePath()==info.absoluteFilePath())//如果是项目文件
            {
                QString projectFileName=topItem->data(0, Qt::UserRole).toString();
                QFileInfo projectInfo(projectFileName);
                executableName = projectInfo.path() + "/" + projectInfo.completeBaseName() + ".exe";
                break;
            }
        }
    }

    //如果没有可执行文件，输出信息并退出。
    if (!QFileInfo::exists(executableName))
    {
        QMessageBox::warning(this, "Warning", tr("No executable file for performance analysis!"));
        return;
    }

    QFileInfo executableFileInfo(executableName);
    //判断是否有gmon.out性能分析文件
    QString gmonName = executableFileInfo.path() + "/gmon.out";
    //如果没有gmon.out文件，输出信息并退出。
    if (!QFileInfo::exists(gmonName))
    {
        QMessageBox::information(this, "Information",
                                tr("The performance data file (gmon.out) is not currently available!")+"\n\n"+
                                tr("The source file should be first compiled in profile mode,")+"\n"+
                                tr("then run the program and exit, gmon.out will be generated."));
        return;
    }

    QFileInfo gmonFileInfo(gmonName);
    if (gmonFileInfo.lastModified()<executableFileInfo.lastModified())
    {
        if (QMessageBox::No == QMessageBox::information(this, "Information", tr("Performance data file is out of date!")+"\n\n"
                                                        +tr("Are you sure to continue profiling?"),QMessageBox::Yes | QMessageBox::No))
            return;
    }

    QDir exeDir(executableFileInfo.path());
    QString exeRelativeName=exeDir.relativeFilePath(executableName);
    QString gmonOut="gmon.out";

    QProcess *process = new QProcess(this);
    process->setWorkingDirectory(executableFileInfo.path());
    addPathsToEnv();
    QString tempFileName = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/GProfilerTempInformationFile";
    process->setStandardOutputFile(tempFileName);
    process->start(gprofFile, QStringList()<<exeRelativeName<<gmonOut);
    bool started = process->waitForStarted();
    if (!started)
    {
        outputInfoEdit->setPlainText("GProf profiler is invalid!");
        QMessageBox::warning(this, "GProf profiler", tr("GProf profiler is invalid!"));
        return;
    }

    process->waitForFinished();

    QByteArray error = process->readAllStandardError();
    QString errorStr = error;
    if (!error.isValidUtf8())
        errorStr = cn(error);

    if (!errorStr.isEmpty())
    {
        QMessageBox::warning(this, "Error in profiling!", errorStr);
        return;
    }

    if(process)
    {
        process->close();
        delete process;
        process = nullptr;//NULL
    }

    QFile formattedFile(tempFileName);
    bool success = formattedFile.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!success) return;

    QString allStr = formattedFile.readAll();
    formattedFile.close();

    allStr=allStr.trimmed();
    if (allStr.isEmpty())
    {
        QMessageBox::warning(this, "Warning", tr("There is no performance data generated!"));
        return;
    }

    QString flatProfileString=allStr;
    QString callGraphString="";
    int indexOfCallGraph=allStr.indexOf("Call graph", Qt::CaseInsensitive);
    if (indexOfCallGraph>=0)
    {
        flatProfileString=allStr.mid(0,indexOfCallGraph).trimmed();
        callGraphString=allStr.mid(indexOfCallGraph);
    }

    QWidget *profileWidget=new QWidget;
    profileWidget->setWindowTitle("Performance Analysis");
    profileWidget->setWindowIcon(QPixmap(":images/profile.png"));
    profileWidget->resize(960,680);
    QHBoxLayout *hLayout=new QHBoxLayout(profileWidget);
    hLayout->setContentsMargins(8, 8, 8, 8);
    QTabWidget *tabWidget=new QTabWidget;
    hLayout->addWidget(tabWidget);

    QString exeFilename=executableFileInfo.fileName();
    QString gmonFilename=gmonFileInfo.fileName();
    int maxLength=std::max(exeFilename.length(), gmonFilename.length());
    exeFilename=QString(exeFilename+"%1").arg(" ", maxLength-exeFilename.length()+1);
    gmonFilename=QString(gmonFilename+"%1").arg(" ", maxLength-gmonFilename.length()+1);

    QString executableFileDataStr=exeFilename+" (last modified at "+executableFileInfo.lastModified().toString("yyyy-MM-dd hh:mm:ss")+")";
    QString gmonFileDateStr=gmonFilename+" (last modified at "+gmonFileInfo.lastModified().toString("yyyy-MM-dd hh:mm:ss")+")";

    flatProfileString=executableFileDataStr+"\n"+gmonFileDateStr+"\n\n"+flatProfileString;

    ProfilerEditor *flatProfileEditor=new ProfilerEditor;
    flatProfileEditor->setReadOnly(true);
    flatProfileEditor->setWordWrapMode(QTextOption::NoWrap);
    flatProfileEditor->setPlainText(flatProfileString);
    if (GlobalVar::colorTheme==0)
    {
        flatProfileEditor->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
        flatProfileEditor->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    }
    else
    {
        flatProfileEditor->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
        flatProfileEditor->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    }

    ProfilerEditor *callGraphEditor=new ProfilerEditor;
    callGraphEditor->setFont(QFont("consolas",13));
    callGraphEditor->setReadOnly(true);
    callGraphEditor->setWordWrapMode(QTextOption::NoWrap);
    callGraphEditor->setPlainText(callGraphString);
    if (GlobalVar::colorTheme==0)
    {
        callGraphEditor->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
        callGraphEditor->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    }
    else
    {
        callGraphEditor->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
        callGraphEditor->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    }

    tabWidget->addTab(flatProfileEditor,"Flat Profile");
    tabWidget->addTab(callGraphEditor,"Call Graph");
    profileWidget->show();

    QFile::remove(tempFileName);
}

QString MainWindow::compile(QString fileName)
{
    QFileInfo info(fileName);
    QString targetFileName = info.path() + "/" + info.completeBaseName() + ".exe";

    //读取编译器列表信息和前次使用的编译器索引号
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QList<CompilerSet> compilerSetList;
    compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
    int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

    //如果没有编译器信息，退出。
    if (compilerSetList.count() == 0)
    {
        QMessageBox::warning(this, "Warning", tr("There is no compiler set!"));
        return tr("There is no compiler set!");
    }
    //确定编译器
    CompilerSet compilerSet;
    if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
    {
        compilerSet = compilerSetList[compilerIndex];
    }
    else
        compilerSet = compilerSetList[0];

    bool compileArgCheckBoxState = compilerSet.compileArgCheckBoxState;
    bool linkArgCheckBoxState = compilerSet.linkArgCheckBoxState;

    QString compiler = compilerSet.gppFile;
    if (QString::compare(info.suffix(), "c", Qt::CaseInsensitive) == 0)
        compiler = compilerSet.gccFile;

    compiler = compiler.trimmed();
    if (compiler.isEmpty())
    {
        QMessageBox::warning(this, "Warning", tr("There is no gcc or g++ compiler!"));
        return tr("There is no gcc or g++ compiler!");
    }

    QStringList arguments;

    arguments << QFileInfo(fileName).fileName();

    arguments << "-o" << QFileInfo(targetFileName).fileName();

    bool warningAllCheckBoxState = compilerSet.warningAllCheckBoxState;
    bool warningExtraCheckBoxState = compilerSet.warningExtraCheckBoxState;
    bool staticLinkCheckBoxState = compilerSet.staticLinkCheckBoxState;
    int modeIndex = compilerSet.modeIndex;

    if (staticLinkCheckBoxState)
        arguments<<"-static";

    arguments<<"-ftabstop=1";

    if (warningAllCheckBoxState)
        arguments<<"-Wall";

    if (warningExtraCheckBoxState)
        arguments<<"-Wextra";

    if (modeIndex==1)
        arguments<<"-g3"<<"-Og";
    else if (modeIndex==2)
        arguments<<"-pg"<<"-no-pie"<<"-DNDEBUG"<<"-O2";
    else if (modeIndex==3)
        arguments<<"-DNDEBUG"<<"-O3";

    arguments<<"-pipe";//编译时使用管道而不是临时文件

    if (QString::compare(info.suffix(), "c", Qt::CaseInsensitive) == 0)
    {
        foreach (QString str, compilerSet.cIncludeList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << ("-I" + trimmedStr);
        }
    }
    else if (QString::compare(info.suffix(), "cpp", Qt::CaseInsensitive) == 0 || QString::compare(info.suffix(), "hpp", Qt::CaseInsensitive) == 0)
    {
        foreach (QString str, compilerSet.cppIncludeList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << ("-I" + trimmedStr);
        }
    }
    else if (QString::compare(info.suffix(), "h", Qt::CaseInsensitive) == 0)
    {
        foreach (QString str, compilerSet.cIncludeList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << ("-I" + trimmedStr);
        }

        foreach (QString str, compilerSet.cppIncludeList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << ("-I" + trimmedStr);
        }
    }
    else
    {
        QMessageBox::warning(this, "Warning", tr("Please save your code as *.c, *.cpp, *.h or *.hpp file!"));
        return tr("Please save your code as *.c, *.cpp, *.h or *.hpp file!");
    }

    QStringList binLibList=compilerSet.binList+compilerSet.libList;
    foreach (QString str, binLibList)
    {
        QString trimmedStr = str.trimmed();
        if (!trimmedStr.isEmpty())
            arguments << ("-L" + trimmedStr);
    }

    arguments << "-L."; //设定当前路径为库搜索路径之一

    if (compileArgCheckBoxState)
    {
        QString compileArg = compilerSet.compileArg.replace("\n", " ");
        QStringList compileArgList = compileArg.split(" ", Qt::SkipEmptyParts);
        foreach (QString str, compileArgList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << trimmedStr;
        }
    }

    if (linkArgCheckBoxState)
    {
        QString linkArg = compilerSet.linkArg.replace("\n", " ");
        QStringList linkArgList = linkArg.split(" ", Qt::SkipEmptyParts);
        foreach (QString str, linkArgList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                arguments << trimmedStr;
        }
    }

    QString command = compiler;
    foreach (QString str, arguments)
        command += (" " + str);

    infoTabWidget->setCurrentIndex(0);
    infoItemModel->removeRows(0, infoItemModel->rowCount());
    infoTableView->repaint();

    if (vSplitter->sizes().at(1) == 0)
        vSplitter->setSizes(splitterSmallSizes);

    QTextCharFormat textCharFormat;
    textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);

    outputInfoEdit->setPlainText(command);

    textCharFormat.setForeground(GlobalVar::stringColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("Compiling ");
    outputInfoEdit->moveCursor(QTextCursor::End);
    outputInfoEdit->repaint();

    //设置相关菜单为非激活状态
    foreach (QAction *action, executeMenu->actions())
        action->setEnabled(false);
    colorThemeMenu->setEnabled(false);
    executeMenu->setEnabled(false);
    projectMenu->setEnabled(false);

    elapsedTimer.start();
    compilingTimer.start(200);

    //定义QFuture<QStringList>数组，用于接收每个线程的数据
    QFuture<QString> future = QtConcurrent::run(&MainWindow::compileThread, this, compiler, arguments, info.path());
    while(!future.isFinished())
    {
        QApplication::processEvents();//不停地处理事件，让程序保持响应
    }
    qint64 compileTime = elapsedTimer.elapsed();
    compilingTimer.stop();

    //相关菜单恢复激活状态
    foreach (QAction *action, executeMenu->actions())
        action->setEnabled(true);
    colorThemeMenu->setEnabled(true);
    executeMenu->setEnabled(true);
    projectMenu->setEnabled(true);

    QString errorStr = future.result();

    int compileErrorNum = 0;
    int linkErrorNum = 0;
    int warningNum = 0;
    int firstErrorRow = -1;
    int firstErrorCol = -1;
    QString firstErrorFile = "";
    if (!errorStr.isEmpty())
    {
        showErrorList(fileName, errorStr, compileErrorNum, linkErrorNum, warningNum, firstErrorRow, firstErrorCol, firstErrorFile);

        if (compileErrorNum > 0)
            textCharFormat.setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
        else
            textCharFormat.setForeground(GlobalVar::warningColor[GlobalVar::colorTheme]);

        outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
        outputInfoEdit->appendPlainText("\n" + errorStr);
    }

    if (warningNum > 0)
        textCharFormat.setForeground(GlobalVar::warningColor[GlobalVar::colorTheme]);
    else
        textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);

    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("------------------------------------");
    outputInfoEdit->appendPlainText("Compiler Warnings: " + QString::number(warningNum));

    if (compileErrorNum > 0)
        textCharFormat.setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
    else
        textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);

    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("Compiler Errors: " + QString::number(compileErrorNum));

    if (linkErrorNum > 0)
        textCharFormat.setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
    else
        textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);

    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("Link Errors: " + QString::number(linkErrorNum));

    textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("Compilation Time: " + QString::number(compileTime / 1000.0, 'f', 2) + "s");
    outputInfoEdit->moveCursor(QTextCursor::End);

    QDateTime currentDateTime = QDateTime::currentDateTime(); //获取系统当前的时间
    QFileInfo executableFileInfo(targetFileName);
    if (executableFileInfo.exists() && compileErrorNum == 0 && linkErrorNum == 0 && qAbs(executableFileInfo.lastModified().secsTo(currentDateTime)) <= 2)
    {
        outputInfoEdit->appendPlainText("Output Filename: " + QFileInfo(targetFileName).fileName());

        double fileSize = executableFileInfo.size();

        QString sizeString = "";
        if (fileSize < 1024 * 1024) sizeString = QString::number(fileSize / 1024.0, 'f', 1) + " KB";
        else if (fileSize < 1024 * 1024 * 1024) sizeString = QString::number(fileSize / 1024.0 / 1024.0, 'f', 3) + " MB";
        else sizeString = QString::number(fileSize / 1024.0 / 1024.0 / 1024.0, 'f', 3) + " GB";

        outputInfoEdit->appendPlainText("Output Size: " + sizeString);
        outputInfoEdit->appendPlainText("Last modified at " + executableFileInfo.lastModified().toString("yyyy/MM/dd hh:mm:ss"));
    }

    if (firstErrorRow >= 0 && !firstErrorFile.isEmpty()&&fileName==firstErrorFile)
        gotoLine(firstErrorFile, firstErrorRow, firstErrorCol, "", false);

    return errorStr;
}

//编译线程，函数内不能有当前主线程的内容，否则编译出错。
QString MainWindow::compileThread(QString compiler, QStringList arguments, QString workingPath)
{
    QProcess process;

    addPathsToEnv();//将可执行文件目录和bin以及lib目录追加到环境变量
    process.setWorkingDirectory(workingPath);

    process.start(compiler, arguments);
    bool started = process.waitForStarted();
    if (!started)
        return compiler + ": fatal error: The compiler is invalid!";

    process.waitForFinished(-1);

    QByteArray error = process.readAllStandardError(); //QByteArray error
    QString newError=byteArrayToString(error);

    return newError;
}

QString MainWindow::buildProject(QString projectFileName, QStringList sourceFiles, QString rcFile, QString precompileFile, ProgramType programType, TargetType targetType)
{
    QString resFile;
    if (!rcFile.isEmpty())
        resFile=QFileInfo(rcFile).completeBaseName()+".res";

    QString gchFile;
    if (!precompileFile.isEmpty())
        gchFile=precompileFile+".gch";

    //读取编译器列表信息和前次使用的编译器索引号
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QList<CompilerSet> compilerSetList;
    compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
    int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

    //如果没有编译器信息，退出。
    if (compilerSetList.count() == 0)
    {
        QMessageBox::warning(this, "Warning", tr("There is no compiler set!"));
        return tr("There is no compiler set!");
    }
    //确定编译器
    CompilerSet compilerSet;
    if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
        compilerSet = compilerSetList[compilerIndex];
    else
        compilerSet = compilerSetList[0];

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    CompilerSet projectCompilerSet=topItem->data(0, Qt::WhatsThisRole).value<CompilerSet>();
    //将项目的编译链接参数复制给当前编译器套件，但不包括编译模式索引modeIndex
    compilerSet.compileArgCheckBoxState=projectCompilerSet.compileArgCheckBoxState;
    compilerSet.linkArgCheckBoxState=projectCompilerSet.linkArgCheckBoxState;
    compilerSet.warningAllCheckBoxState=projectCompilerSet.warningAllCheckBoxState;
    compilerSet.warningExtraCheckBoxState=projectCompilerSet.warningExtraCheckBoxState;
    compilerSet.staticLinkCheckBoxState=projectCompilerSet.staticLinkCheckBoxState;
    compilerSet.compileArg=projectCompilerSet.compileArg;
    compilerSet.linkArg=projectCompilerSet.linkArg;

    if (notEqualCompilerSet(projectCompilerSet, compilerSet))
    {
        clearTempFiles(QStringList{"*.o", "*.res", "*.gch"});
        QVariant variant;
        variant.setValue(compilerSet);
        topItem->setData(0, Qt::WhatsThisRole, variant);
    }

    bool compileArgCheckBoxState = compilerSet.compileArgCheckBoxState;
    bool linkArgCheckBoxState = compilerSet.linkArgCheckBoxState;
    bool warningAllCheckBoxState = compilerSet.warningAllCheckBoxState;
    bool warningExtraCheckBoxState = compilerSet.warningExtraCheckBoxState;
    bool staticLinkCheckBoxState = compilerSet.staticLinkCheckBoxState;
    int modeIndex = compilerSet.modeIndex;    

    QString compiler=compilerSet.gppFile.trimmed();
    if (compiler.isEmpty()) compiler="g++.exe";

    if (programType==CProgram)
    {
        compiler=compilerSet.gccFile.trimmed();
        if (compiler.isEmpty()) compiler="gcc.exe";
    }

    QString makeFileStr;
    makeFileStr+=("COMPILER="+compiler+"\n\n");
    QString windres=compilerSet.windresFile.trimmed();
    if (windres.isEmpty()) windres="windres.exe";
    makeFileStr+=("WINDRES="+windres+"\n\n");
    makeFileStr+=("OBJS=");
    foreach (QString fileName, sourceFiles)
    {
        QFileInfo info(fileName);
        QString objectName = info.path()+"/"+info.completeBaseName() + ".o";
        if (info.path()==".")
            objectName = info.completeBaseName() + ".o";
        if (objectName.contains(" "))
            objectName="\""+objectName.replace(" ", "\\ ")+"\"";

        makeFileStr+=objectName+" ";
    }

    if (resFile.contains(" "))
    {
        QString tempStr=resFile;
        makeFileStr+="\""+tempStr.replace(" ", "\\ ")+"\"";
    }
    else
    {
        makeFileStr+=resFile;
    }

    makeFileStr+=("\n\n");
    makeFileStr+=("LINKOBJS=");
    foreach (QString fileName, sourceFiles)
    {
        QFileInfo info(fileName);
        QString objectName = info.path()+"/"+info.completeBaseName() + ".o";
        if (info.path()==".")
            objectName = info.completeBaseName() + ".o";
        if (objectName.contains(" "))
            objectName="\""+objectName+"\"";

        makeFileStr+=objectName+" ";
    }

    if (resFile.contains(" "))
        makeFileStr+="\""+resFile+"\"";
    else
        makeFileStr+=resFile;

    makeFileStr+=("\n\n");
    makeFileStr+=("LIBS=");//lib链接库目录
    QStringList binLibList=compilerSet.libList+compilerSet.binList;
    foreach (QString str, binLibList)
    {
        QString trimmedStr = str.trimmed();
        makeFileStr+="-L\""+trimmedStr+"\" ";
    }

    makeFileStr+="-L. "; //设定当前路径为库搜索路径之一

    if (staticLinkCheckBoxState) makeFileStr+="-static ";
    if (targetType==DLL) makeFileStr+="-shared ";

    if (linkArgCheckBoxState)//链接选项
    {
        QString linkArg = compilerSet.linkArg.replace("\n", " ");
        QStringList linkArgList = linkArg.split(" ", Qt::SkipEmptyParts);
        foreach (QString str, linkArgList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                makeFileStr+=trimmedStr+" ";
        }
    }

    if (compileArgCheckBoxState) //编译选项
    {
        QString compileArg = compilerSet.compileArg.replace("\n", " ");
        QStringList compileArgList = compileArg.split(" ", Qt::SkipEmptyParts);
        foreach (QString str, compileArgList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                makeFileStr+=trimmedStr+" ";
        }
    }

    if (modeIndex==1)
        makeFileStr+="-g3 -Og ";
    else if (modeIndex==2)
        makeFileStr+="-pg -no-pie -DNDEBUG -O2 ";
    else if (modeIndex==3)
        makeFileStr+="-DNDEBUG -O3 ";

    makeFileStr+="-ftabstop=1 ";//设定tab键为1个字符

    //makeFileStr+="-Winvalid-pch ";//预编译头不可用时发出警告
    makeFileStr+="-pipe ";//编译时使用管道而不是临时文件。

    if (warningAllCheckBoxState) makeFileStr+="-Wall ";
    if (warningExtraCheckBoxState) makeFileStr+="-Wextra ";

    makeFileStr+=("\n\n");
    makeFileStr+=("INCS=");//include包含目录
    QStringList includeList=compilerSet.cppIncludeList;
    if (programType==CProgram)
        includeList=compilerSet.cIncludeList;

    foreach (QString str, includeList)
    {
        QString trimmedStr = str.trimmed();
        makeFileStr+="-I\""+trimmedStr+"\" ";
    }

    makeFileStr+=("\n\n");
    makeFileStr+=("FLAGS=$(INCS) ");//include目录加上编译选项

    if (targetType==DLL)
    {
        makeFileStr+="-fPIC ";
        clearTempFiles(QStringList{"*.o"});//删除中间文件，重新用-fPIC编译
    }

    if (modeIndex==1)
        makeFileStr+="-g3 -Og ";
    else if (modeIndex==2)
        makeFileStr+="-pg -no-pie -DNDEBUG -O2 ";
    else if (modeIndex==3)
        makeFileStr+="-DNDEBUG -O3 ";

    makeFileStr+="-ftabstop=1 ";//设定tab键为1个字符

    if (warningAllCheckBoxState) makeFileStr+="-Wall ";
    if (warningExtraCheckBoxState) makeFileStr+="-Wextra ";

    //makeFileStr+="-Winvalid-pch ";//预编译头不可用时发出警告
    makeFileStr+="-pipe ";//编译时使用管道而不是临时文件。

    if (compileArgCheckBoxState)
    {
        QString compileArg = compilerSet.compileArg.replace("\n", " ");
        QStringList compileArgList = compileArg.split(" ", Qt::SkipEmptyParts);
        foreach (QString str, compileArgList)
        {
            QString trimmedStr = str.trimmed();
            if (!trimmedStr.isEmpty())
                makeFileStr+=trimmedStr+" ";
        }
    }

    makeFileStr+="\n\n";
    QString targetFile=QFileInfo(projectFileName).completeBaseName()+".exe";
    if (targetType==DLL) targetFile=QFileInfo(projectFileName).completeBaseName()+".dll";
    else if (targetType==LIB) targetFile=QFileInfo(projectFileName).completeBaseName()+".lib";

    QString targetFileMK=targetFile;
    if (targetFileMK.contains(" "))
        targetFileMK="\""+targetFileMK+"\"";

    makeFileStr+="TARGET = "+targetFileMK +"\n\n";

    makeFileStr+="$(TARGET) : "+(gchFile.isEmpty()?"":gchFile+" ")+"$(OBJS) \n";

    if (targetType==LIB)
        makeFileStr+="\tar -rcs $(TARGET) $(LINKOBJS)\n\n";
    else
        makeFileStr+="\t$(COMPILER) $(LINKOBJS) -o $(TARGET) $(LIBS)\n\n";

    if (!precompileFile.isEmpty())
    {
        makeFileStr+=gchFile+" : "+precompileFile+"\n";
        makeFileStr+="\t$(COMPILER) -c "+precompileFile+" -o "+gchFile+" $(FLAGS) \n\n";
    }

    foreach (QString fileName, sourceFiles)
    {
        QDir projectDir=QFileInfo(projectFileName).path();
        QString absoluteFileName=fileName;
        if (QFileInfo(fileName).isRelative())
            absoluteFileName=projectDir.absoluteFilePath(fileName);

        QString absoluteObjectName=QFileInfo(absoluteFileName).path()+"/"+QFileInfo(absoluteFileName).completeBaseName()+".o";
        //获得各个源文件的包含文件列表
        QStringList headers=getDependentHeaders(absoluteFileName);
        QString absoluteHeader;
        QString dependentHeaders;
        QString tempHeader;
        QDir sourceFileDir(QFileInfo(absoluteFileName).path());
        foreach (QString header, headers)
        {
            absoluteHeader=header;
            if (QFileInfo(header).isRelative())
                absoluteHeader=sourceFileDir.absoluteFilePath(header);

            if (!QFileInfo::exists(absoluteHeader))
            {
                QFile::remove(absoluteObjectName);
            }
            else
            {
                tempHeader=projectDir.relativeFilePath(absoluteHeader);
                if (tempHeader.contains(" "))
                    tempHeader.replace(" ","\\ ");
                dependentHeaders+=(tempHeader+" ");
            }
        }

        QFileInfo info(fileName);
        QString objectName = info.path()+"/"+info.completeBaseName() + ".o";
        if (info.path()==".")
            objectName = info.completeBaseName() + ".o";
        QString objectNameQuote;
        QString objectNameQuoteSlash;
        QString fileNameQuote;
        QString fileNameSlash;
        if (objectName.contains(" "))
        {
            objectNameQuote="\""+objectName+"\"";
            objectNameQuoteSlash="\""+objectName.replace(" ", "\\ ")+"\"";
            fileNameQuote="\""+fileName+"\"";
            fileNameSlash=fileName.replace(" ", "\\ ");
            makeFileStr+=objectNameQuoteSlash+" : "+fileNameSlash+" "+(precompileFile.isEmpty()?"":precompileFile+" ")+dependentHeaders+"\n";
            makeFileStr+="\t$(COMPILER) -c "+fileNameQuote+(precompileFile.isEmpty()?"":" -include "+precompileFile)+" -o "+objectNameQuote+" $(FLAGS) \n\n";
        }
        else
        {
            makeFileStr+=objectName+" : "+fileName+" "+(precompileFile.isEmpty()?"":precompileFile+" ")+dependentHeaders+"\n";
            makeFileStr+="\t$(COMPILER) -c "+fileName+(precompileFile.isEmpty()?"":" -include "+precompileFile)+" -o "+objectName+" $(FLAGS)\n\n";
        }
    }

    if (!rcFile.isEmpty())
    {
        if (rcFile.contains(" "))
        {
            QString rcWithQuota="\""+rcFile+"\"";
            QString rcWithSlash=rcFile.replace(" ","\\ ");
            QString resWithoutSlash="\""+resFile+"\"";
            QString resWithSlash="\""+resFile.replace(" ","\\ ")+"\"";

            makeFileStr+=resWithSlash+" : "+ rcWithSlash+ "\n";
            makeFileStr+="\t$(WINDRES) "+rcWithQuota+" -O coff -o "+resWithoutSlash+"\n\n";
        }
        else
        {
            makeFileStr+=resFile+" : "+rcFile+"\n";
            makeFileStr+="\t$(WINDRES) "+rcFile+" -O coff -o "+resFile+"\n\n";
        }
    }

    makeFileStr+=".PHONY : clean\n";
    makeFileStr+="clean:\n";
    makeFileStr+="\tdel $(TARGET) $(OBJS) *.res *.gch\n";//文件必须是本地目录形式，命令形式为 "mingw32-make -f makefile.mk clean"

    QString makeFileName=QFileInfo(projectFileName).path()+"/Makefile.mk";
    QFile file(makeFileName); //file.write(str.toUtf8()); //这种形式更简洁。
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);
        QStringConverter::Encoding encoding = QStringConverter::System;
        out.setEncoding(encoding);
        out << makeFileStr;
        file.close();
    }
    else
    {
        QMessageBox::warning(this, tr("Save Makefile Error"), tr("Failed to write to makefile!"));
        return "Failed to write to makefile!";
    }

    infoTabWidget->setCurrentIndex(0);
    infoItemModel->removeRows(0, infoItemModel->rowCount());
    infoTableView->repaint();

    if (vSplitter->sizes().at(1) == 0)
        vSplitter->setSizes(splitterSmallSizes);

    QTextCharFormat textCharFormat;
    textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);

    textCharFormat.setForeground(GlobalVar::stringColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->moveCursor(QTextCursor::End);
    outputInfoEdit->repaint();

    textCharFormat.setForeground(GlobalVar::stringColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);

    elapsedTimer.start();

    QString makeCommand=compilerSet.makeFile.trimmed();
    if (makeCommand.isEmpty())
        makeCommand="mingw32-make.exe";

    //定义QFuture<QStringList>，用于接收线程的数据
    QFuture<QString> future = QtConcurrent::run(&MainWindow::buildThread, this, makeCommand, QStringList{"-j1", "-f", makeFileName}, QFileInfo(projectFileName).path());
    while(!future.isFinished())
        QApplication::processEvents();//不停地处理事件，让程序保持响应

    qint64 compileTime = elapsedTimer.elapsed();
    compilingTimer.stop();

    QString errorStr = future.result().trimmed();

    int compileErrorNum = 0;
    int linkErrorNum = 0;
    int warningNum = 0;
    int firstErrorRow = -1;
    int firstErrorCol = -1;
    QString firstErrorFile = "";

    if (!errorStr.isEmpty())
    {
        showErrorList(projectFileName, errorStr, compileErrorNum, linkErrorNum, warningNum, firstErrorRow, firstErrorCol, firstErrorFile);

        if (compileErrorNum > 0)
            textCharFormat.setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
        else
            textCharFormat.setForeground(GlobalVar::warningColor[GlobalVar::colorTheme]);

        outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
        outputInfoEdit->appendPlainText("\n" + errorStr);
    }

    if (warningNum > 0)
        textCharFormat.setForeground(GlobalVar::warningColor[GlobalVar::colorTheme]);
    else
        textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);

    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);

    outputInfoEdit->appendPlainText("------------------------------------");
    if (warningNum>0)
        outputInfoEdit->appendPlainText("Compiler Warnings: " + QString::number(warningNum));

    if (compileErrorNum > 0)
        textCharFormat.setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
    else
        textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);

    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    if (compileErrorNum > 0)
        outputInfoEdit->appendPlainText("Compiler Errors: " + QString::number(compileErrorNum));
    if (linkErrorNum > 0)
        outputInfoEdit->appendPlainText("Link Errors: " + QString::number(linkErrorNum));

    textCharFormat.setForeground(GlobalVar::foregroundColor[GlobalVar::colorTheme]);
    outputInfoEdit->mergeCurrentCharFormat(textCharFormat);
    outputInfoEdit->appendPlainText("Compilation Time: " + QString::number(compileTime / 1000.0, 'f', 2) + "s");
    outputInfoEdit->moveCursor(QTextCursor::End);

    QDateTime currentDateTime = QDateTime::currentDateTime(); //获取系统当前的时间
    QFileInfo projectFileInfo(projectFileName);
    QString targetFileName=projectFileInfo.path()+"/"+targetFile;
    QFileInfo targetFileInfo(targetFileName);
    if (targetFileInfo.exists() && qAbs(targetFileInfo.lastModified().secsTo(currentDateTime)) <= 2)
    {
        outputInfoEdit->appendPlainText("Output Filename: " + targetFile);

        double fileSize = targetFileInfo.size();
        QString sizeString = "";
        if (fileSize < 1024 * 1024) sizeString = QString::number(fileSize / 1024.0, 'f', 1) + " KB";
        else if (fileSize < 1024 * 1024 * 1024) sizeString = QString::number(fileSize / 1024.0 / 1024.0, 'f', 3) + " MB";
        else sizeString = QString::number(fileSize / 1024.0 / 1024.0 / 1024.0, 'f', 3) + " GB";

        outputInfoEdit->appendPlainText("Output Size: " + sizeString);
        outputInfoEdit->appendPlainText("Last modified at " + targetFileInfo.lastModified().toString("yyyy/MM/dd hh:mm:ss"));
    }

    if (firstErrorRow >= 0 && !firstErrorFile.isEmpty())
    {
        QSettings setting(projectFileName, QSettings::IniFormat);
        QStringList sourceFileList = setting.value("SourceFiles").value<QStringList>();

        if (sourceFileList.contains(QFileInfo(firstErrorFile).fileName(), Qt::CaseInsensitive)) //如果是项目中的文件，则打开文件跳转到相应的错误处。
            gotoLine(firstErrorFile, firstErrorRow, firstErrorCol, "", false);
    }

    return errorStr;
}

QString MainWindow::buildThread(QString make, QStringList makeFile, QString workingPath)
{
    QProcess process;
    connect(&process, &QProcess::readyReadStandardOutput, &process, [this, &process]()
    {
        QByteArray outputStr=process.readAllStandardOutput();
        emit this->sendBuildOutput(outputStr);
    });

    addPathsToEnv();//将可执行文件目录和bin以及lib目录追加到环境变量
    process.setWorkingDirectory(workingPath);

    process.start(make, makeFile);
    bool started = process.waitForStarted();
    if (!started)
        return tr("Fatal error: %1 command is invalid!").arg(make);

    process.waitForFinished(-1);

    QByteArray error = process.readAllStandardError(); //QByteArray error
    QString errorStr=byteArrayToString(error);

    return errorStr;
}

QString MainWindow::byteArrayToString(QByteArray byteArray)
{
    QList<QByteArray> arrayList=byteArray.split('\n');
    QString newString;
    QString tempString;
    foreach (QByteArray array, arrayList)
    {
        tempString=array.isValidUtf8()? array : cn(array);
        newString+=(tempString+"\n");
    }

    if (arrayList.count()) newString=newString.removeLast();

    return newString;
}

void MainWindow::compilingProgress()
{
    outputInfoEdit->moveCursor(QTextCursor::End);
    outputInfoEdit->insertPlainText("\u00BB");
}

void MainWindow::showErrorList(QString fileName, QString errorStr, int& compileErrorNum, int& linkErrorNum, int& warningNum, int& firstErrorRow, int& firstErrorCol, QString& firstErrorFile)
{
    QStringList errorList = errorStr.split('\n');
    static QRegularExpression re1("(.*?)(:\\d+)?(:\\d+)?: ([A-Za-z ]*error: |warning: |note: )?(.*)?");
    QRegularExpressionMatch match;
    infoItemModel->removeRows(0, infoItemModel->rowCount());//删除所有内容
    if (errorList.size()>0)
        infoTabWidget->setCurrentIndex(1);

    QVector<CompileResult> results;
    CompileResult result;

    foreach (QString str, errorList)
    {
        if (str.size() > 0 && str[0] == ' ') continue;

        match = re1.match(str, 0);
        if (match.hasMatch())
        {
            QDir fileDir(QFileInfo(fileName).path());
            QString absoluteFileName=match.captured(1);
            QFileInfo absoluteFileInfo(absoluteFileName);
            if (absoluteFileInfo.isRelative())
                absoluteFileName=fileDir.absoluteFilePath(absoluteFileName);

            QFileInfo fileInfo(absoluteFileName);

            result.fileName=absoluteFileName;

            if (fileInfo.fileName().compare("ld.exe",Qt::CaseInsensitive)==0)
                linkErrorNum++;

            result.row=match.captured(2).remove(':');

            result.column=match.captured(3).remove(':');

            QString captured4 = match.captured(4).trimmed();
            captured4 = captured4.remove(":");

            result.type=captured4;

            result.message=match.captured(5);

            results<<result;

            if (captured4.contains("error", Qt::CaseInsensitive))
            {
                compileErrorNum++;
                if (compileErrorNum == 1)
                {
                    bool hasRowNum = false;
                    int tempRow = result.row.toInt(&hasRowNum);

                    bool hasColNum = false;
                    int tempCol = result.column.toInt(&hasColNum);
                    if (!hasColNum) tempCol = 0;

                    if (hasRowNum && !match.captured(1).isEmpty())
                    {
                        firstErrorRow = tempRow;
                        firstErrorCol = tempCol;
                        firstErrorFile = fileInfo.absoluteFilePath();
                    }
                }
            }
            else if (QString::compare(captured4, "warning", Qt::CaseInsensitive) == 0)
            {
                warningNum++;
            }
        }
    }

    infoTableView->setUpdatesEnabled(false);

    int resultNum=results.size();
    infoItemModel->setRowCount(resultNum);
    infoItemModel->setColumnCount(5);
    //填写编译结果信息到infoTableView
    for (int i=0; i<resultNum; i++)
    {
        infoItemModel->setData(infoItemModel->index(i, 0), QFileInfo(results[i].fileName).fileName(), Qt::DisplayRole);
        infoItemModel->setData(infoItemModel->index(i, 0), results[i].fileName, Qt::ToolTipRole);

        infoItemModel->setData(infoItemModel->index(i, 1), results[i].row, Qt::DisplayRole);
        infoItemModel->item(i,1)->setTextAlignment(Qt::AlignCenter);

        infoItemModel->setData(infoItemModel->index(i, 2), results[i].column, Qt::DisplayRole);
        infoItemModel->item(i,2)->setTextAlignment(Qt::AlignCenter);

        infoItemModel->setData(infoItemModel->index(i, 3), results[i].type, Qt::DisplayRole);
        infoItemModel->item(i,3)->setTextAlignment(Qt::AlignCenter);

        infoItemModel->setData(infoItemModel->index(i, 4), results[i].message, Qt::DisplayRole);

        if (results[i].type.contains("error", Qt::CaseInsensitive))
        {
            for (int n=0; n<5; n++)
                infoItemModel->item(i,n)->setForeground(GlobalVar::errorColor[GlobalVar::colorTheme]);
        }
        else if (QString::compare(results[i].type, "warning", Qt::CaseInsensitive) == 0)
        {
            for (int n=0; n<5; n++)
                infoItemModel->item(i,n)->setForeground(GlobalVar::warningColor[GlobalVar::colorTheme]);
        }
    }

    infoTableView->setUpdatesEnabled(true);
}

void MainWindow::runMenuClicked()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    QString fileName = editor->documentTitle();
    QFileInfo info(fileName);

    //检测是否是单个文件还是项目文件
    bool isSingleFile=true;
    if (treeWidget->topLevelItemCount()!=0)
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        int childCount=topItem->childCount();
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *subItem=topItem->child(i);
            QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
            if (subInfo.absoluteFilePath()==info.absoluteFilePath())
            {
                isSingleFile=false;
                break;
            }
        }
    }

    if (!isSingleFile&&treeWidget->topLevelItemCount()>0)//如果不是单个文件，按照项目寻找可执行文件。
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        QString projectFileName=topItem->data(0, Qt::UserRole).toString();
        QFileInfo projectInfo(projectFileName);
        QString executableName = projectInfo.path() + "/" + projectInfo.completeBaseName() + ".exe";
        run(executableName);
        return;
    }

    if (!info.exists())
    {
        QMessageBox::warning(this, "Information", fileName + "\n\n"+tr("The above file does not exist!"));
        return;
    }

    if (!fileName.endsWith(".c", Qt::CaseInsensitive) && !fileName.endsWith(".cpp", Qt::CaseInsensitive))
    {
        QMessageBox::warning(this, "Compile&&Run", tr("Only *.c or *.cpp file has related exe file!"));
        return;
    }

    QString executableName = info.path() + "/" + info.completeBaseName() + ".exe";

    run(executableName);
}

void MainWindow::run(QString executableName)
{
    if (!QFileInfo::exists(executableName))
    {
        QMessageBox::information(this, "Information", tr("The related executable file does not exist!"));
        return;
    }

    try
    {
        QProcess *process=new QProcess(this);
        addPathsToEnv();//将可执行文件目录和bin以及lib目录追加到环境变量

        process->setWorkingDirectory(QFileInfo(executableName).path());

        QFileInfo consolePauserInfo(QApplication::applicationDirPath() + "/consolepauser.exe");
        if (programHasConsole(executableName) && consolePauserInfo.exists())
        {
            process->setProgram(consolePauserInfo.absoluteFilePath());
            process->setArguments(QStringList{executableName});
        }
        else
        {
            process->setProgram(executableName);
        }

        process->closeWriteChannel();
        process->setCreateProcessArgumentsModifier([] (QProcess::CreateProcessArguments * args)
                                                   {
                                                       args->flags |= CREATE_NEW_CONSOLE;
                                                       args->flags &= ~CREATE_NO_WINDOW;
                                                       args->startupInfo->dwFlags &= ~STARTF_USESTDHANDLES;
                                                       args->startupInfo->dwFlags |= STARTF_USEFILLATTRIBUTE;
                                                       args->startupInfo->dwFillAttribute = BACKGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
                                                   });

        process->start();
    }
    catch(const std::runtime_error& e)
    {
        QMessageBox::critical(this, "Runtime Error", e.what());
    }
    catch(const std::invalid_argument& e)
    {
        QMessageBox::critical(this, "invalid_argument", e.what());
    }
    catch(std::exception &e)
    {
        QMessageBox::critical(this, "Critical Error", e.what());
    }
    catch(...)
    {
        QMessageBox::critical(this, "Critical Error", tr("Error occurred in running program!"));
    }
}

void MainWindow::addPathsToEnv()
{
    //读取编译器列表信息和前次使用的编译器索引号
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QList<CompilerSet> compilerSetList;
    compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
    int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

    CompilerSet compilerSet;
    //确定编译器
    if (compilerSetList.count() > 0)
    {
        if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
            compilerSet = compilerSetList[compilerIndex];
        else
            compilerSet = compilerSetList[0];
    }

    QStringList reverseBinList;
    int binListCount=compilerSet.binList.count();
    for (int i=binListCount-1;i>=0; i--)
        reverseBinList.append(compilerSet.binList[i]);

    QStringList libBinList=reverseBinList+compilerSet.libList;
    QString pathsAddToEnv;
    QString nativePath;
    QString envPath=qgetenv("PATH").constData();
    QStringList envPathList=envPath.split(';', Qt::SkipEmptyParts);

    foreach (QString path, libBinList)//将bin和lib目录列表追加到环境变量中
    {
        nativePath=QDir::toNativeSeparators(path);
        envPathList.removeAll(nativePath);
        pathsAddToEnv+=(";"+nativePath);
    }

    if (!pathsAddToEnv.isEmpty())
    {
        if (pathsAddToEnv[0]==';') pathsAddToEnv.removeFirst();
        QString finalEnvPath=QString("%1;%2").arg(pathsAddToEnv, envPathList.join(";"));
        qputenv("PATH",finalEnvPath.toUtf8());
    }
}

void MainWindow::buildRun()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    QString fileName = editor->documentTitle();
    QFileInfo fileInfo(fileName);
    //检测是否是单个文件还是项目文件
    bool isSingleFile=true;
    if (treeWidget->topLevelItemCount()!=0)
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        int childCount=topItem->childCount();
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *subItem=topItem->child(i);
            QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
            if (subInfo.absoluteFilePath()==fileInfo.absoluteFilePath())
            {
                isSingleFile=false;
                break;
            }
        }
    }

    QAction *clickedAction=dynamic_cast<QAction*>(sender());

    if (isSingleFile)//如果是单个文件，按照单文件编译执行。
    {
        if (clickedAction==buildDllAction||clickedAction==buildLibAction)
        {
            QMessageBox::information(this,
                                     tr("Build DLL&LIB Info"),
                                     tr("Single file can not be built to shared or statically linked library. "
                                        "Please create new project and then add new file or new class to project. "
                                        "After finishing debug, make main file codes to comment, "
                                        "then click \"Build DLL\" or \"Build LIB\" to create library. "
                                        "The header file and generated dll or lib file can be used by other programs."));
            return;
        }
        compileRun(clickedAction==compileRunAction);
        return;
    }

    if (treeWidget->topLevelItemCount()==0) return;

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    int childCount=topItem->childCount();
    QDateTime currentTime = QDateTime::currentDateTime(); //获取系统当前的时间
    //保存项目中有修改的文件
    for (int i=0; i<childCount; i++)
    {
        QTreeWidgetItem *subItem=topItem->child(i);
        QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());

        for (int j = 0; j < mainTabWidget->count(); j++)
        {
            QWidget *widget = mainTabWidget->widget(j);
            CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
            QString docTitle = editor->documentTitle();
            if (QFileInfo(docTitle).absoluteFilePath()==subInfo.absoluteFilePath()&&editor->document()->isModified())
            {
                bool savedSuccessfully = save(editor->toPlainText(), docTitle);
                if (savedSuccessfully)
                    editor->document()->setModified(false);
                else
                {
                    QMessageBox::warning(this, tr("Error in file saving"), fileName + "\n\n"+tr("Failed in saving the above file!"));
                    return;
                }
            }
        }
    }    

    outputInfoEdit->setPlainText("");

    QString projectFile=topItem->data(0, Qt::UserRole).toString();
    QDir projectDir(QFileInfo(projectFile).path());
    QStringList sourceList;
    QStringList rcList;
    QStringList precompileList;
    QStringList nonCodeFiles;
    QStringList allProjectFileList;
    QStringList allProjectRelativeFileList;
    int subItemCount=topItem->childCount();
    for (int i=0; i<subItemCount; i++)
    {
        QString fileName=topItem->child(i)->data(0, Qt::UserRole).toString();
        allProjectFileList<<fileName;
        allProjectRelativeFileList<<projectDir.relativeFilePath(fileName);

        if (fileName.endsWith(".c", Qt::CaseInsensitive)||fileName.endsWith(".cpp", Qt::CaseInsensitive)||fileName.endsWith(".cc", Qt::CaseInsensitive)||fileName.endsWith(".cxx", Qt::CaseInsensitive))
            sourceList<<projectDir.relativeFilePath(fileName);
        else if (fileName.endsWith(".rc", Qt::CaseInsensitive))
            rcList<<projectDir.relativeFilePath(fileName);
        else if (fileName.endsWith(".h")||fileName.endsWith(".hxx")||fileName.endsWith(".hpp"))
        {
            if (topItem->child(i)->data(0, Qt::WhatsThisRole).toString()=="Precompile")
                precompileList<<projectDir.relativeFilePath(fileName);;
        }
        else
            nonCodeFiles<<fileName;
    }

    if (rcList.count()>1)
    {
        QMessageBox::warning(this, tr("Multiple RC files!"), tr("Only one resource file (*.rc) is allowed in project!%1Please remove unnecessary resource files!").arg("\n"));
        return;
    }

    if (precompileList.count()>1)
    {
        QMessageBox::warning(this, tr("Multiple precompile files!"), tr("Only one precompile header file is allowed in project!%1Please remove unnecessary precompile header files!").arg("\n"));
        return;
    }

    QString rcFile;
    if (rcList.count()>0)
        rcFile=rcList[0];

    QString precompileFile;
    if (precompileList.count()>0)
        precompileFile=precompileList[0];

    if (nonCodeFiles.count()>0)
    {
        QString nonCodeFilesString;
        foreach (QString str, nonCodeFiles)
            nonCodeFilesString+=(str+"\n");
        QMessageBox::warning(this, tr("Non code files warning!"), tr("%1The above files may not be able to built into project, "
                                                                     "because only *.c, *.cpp, *.cxx, *.cc, *.h, *.hxx, *.hpp, *.rc files "
                                                                     "are treated as code files. Please rename or remove the above files.").arg(nonCodeFilesString+"\n"));
    }

    QFileInfo projectInfo(projectFile);
    QString exeName=projectInfo.completeBaseName()+".exe";

    //列出系统所有进程，如果有本程序在运行，则结束本程序的运行，包括consolewindow。
    QProcess proc;
    proc.start("tasklist");//获得进程列表
    proc.waitForFinished();// 等待 tasklist 启动
    QString result = proc.readAllStandardOutput();
    if(result.contains("WindowsTerminal.exe"))//如果consolepauser在运行，结束它。
    {
        QStringList params1;
        params1 <<"-f"<<"-im"<<"WindowsTerminal.exe";
        proc.startDetached("taskkill",params1);
        proc.waitForFinished();
    }
    if(result.contains(exeName))//如果有本程序在运行，终止执行。
    {
        QStringList params;
        params <<"-f"<<"-im"<<exeName;
        proc.startDetached("taskkill",params);
        proc.waitForFinished();
    }

    ProgramType programType=topItem->data(0, Qt::StatusTipRole).value<ProgramType>();
    TargetType targetType=EXE;
    if (clickedAction==buildDllAction)
        targetType=DLL;
    else if (clickedAction==buildLibAction)
        targetType=LIB;

    foreach (QString fileName, allProjectRelativeFileList)
    {
        if (fileName.contains(" "))
        {
            QMessageBox::warning(this, "Warning!", tr("%1White spaces are not allowed in the above file path or file name!").arg(fileName+"\n\n"));
            return;
        }
    }

    foreach (QAction *action, executeMenu->actions())
        action->setEnabled(false);

    colorThemeMenu->setEnabled(false);
    executeMenu->setEnabled(false);
    projectMenu->setEnabled(false);
    this->repaint();

    //如果项目文件编码格式和Cute C++的当前文件编码格式不同，则重新读取并保存所有项目文件。
    bool ok=false;
    int projectEncodingIndex=topItem->data(0, Qt::AccessibleTextRole).toInt(&ok);
    if (!ok) projectEncodingIndex=-1;
    if (projectEncodingIndex!=encodingIndex)
    {
        foreach (QString fileName, allProjectFileList)
        {
            QFileInfo fileInfo(fileName);
            if (fileInfo.exists()&&fileInfo.lastModified()<currentTime)
            {
                bool success=false;
                QString source=readStringFromFile(fileName, success);
                if (success&&!source.isEmpty())
                    save(source, fileName);
            }
        }

        topItem->setData(0, Qt::AccessibleTextRole, encodingIndex);//设定项目文件编码格式为当前编码格式
    }

    QString errorStr=buildProject(projectFile, sourceList, rcFile, precompileFile, programType, targetType);

    foreach (QAction *action, executeMenu->actions())
        action->setEnabled(true);

    colorThemeMenu->setEnabled(true);
    executeMenu->setEnabled(true);
    projectMenu->setEnabled(true);

    QFileInfo projectFileInfo(projectFile);
    QString execFileName=projectFileInfo.path()+"/"+projectFileInfo.completeBaseName()+".exe";
    QFileInfo exeFileInfo(execFileName);
    if (!exeFileInfo.exists()) //如果可执行文件不存在，返回。
    {
        outputInfoEdit->appendPlainText("No executable file generated!");
        return;
    }

    if (clickedAction==compileRunAction)//如果是编译执行菜单项被点击，则执行程序。
    {
        QDateTime currentDateTime = QDateTime::currentDateTime(); //获取系统当前的时间
        if (errorStr.isEmpty()||qAbs(exeFileInfo.lastModified().secsTo(currentDateTime)) <= 2)
            run(exeFileInfo.absoluteFilePath());
    }
}

void MainWindow::compileRun(bool needRun)
{
    if (mainTabWidget->count() < 1) return;

    outputInfoEdit->setPlainText("");

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

    saveFile(editor);

    QString fileName = editor->documentTitle();
    QFileInfo info(fileName);

    if (!info.exists())
    {
        QMessageBox::warning(this, "Compile", tr("Please save your code as *.c, *.cpp, *.h or *.hpp file!"));
        return;
    }

    if (!fileName.endsWith(".c", Qt::CaseInsensitive) && !fileName.endsWith(".cpp", Qt::CaseInsensitive))
    {
        QMessageBox::warning(this, "Compile&&Run", tr("Only *.c or *.cpp file can be compiled to exe file!"));
        return;
    }

    QFileInfo fileInfo(fileName);
    QString exeName=fileInfo.completeBaseName()+".exe";

    //列出系统所有进程，如果有本程序在运行，则结束本程序的运行，包括consolewindow。
    QProcess proc;
    proc.start("tasklist");//获得进程列表
    proc.waitForFinished();// 等待 tasklist 启动
    QString result = proc.readAllStandardOutput();
    if(result.contains("WindowsTerminal.exe"))//如果consolepauser在运行，结束它。
    {
        QStringList params1;
        params1 <<"-f"<<"-im"<<"WindowsTerminal.exe";
        proc.startDetached("taskkill",params1);
        proc.waitForFinished();
    }
    if(result.contains(exeName))//如果有本程序在运行，终止执行。
    {
        QStringList params;
        params <<"-f"<<"-im"<<exeName;
        proc.startDetached("taskkill",params);
        proc.waitForFinished();
    }

    QString executableName = info.path() + "/" + info.completeBaseName() + ".exe";

    QString errorStr = compile(fileName);

    if (!QFileInfo::exists(executableName)) //如果可执行文件不存在，返回。
    {
        outputInfoEdit->appendPlainText("No executable file generated!");
        return;
    }

    if (needRun)
    {
        QDateTime currentDateTime = QDateTime::currentDateTime(); //获取系统当前的时间
        QDateTime executeDateTime =QFileInfo(executableName).lastModified();//获取可执行程序最近修改的时间

        if (errorStr.isEmpty() || qAbs(executeDateTime.secsTo(currentDateTime)) <= 2)
            run(executableName);
    }
}

void MainWindow::undo()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->undo();
    }
}

void MainWindow::redo()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->redo();
    }
}

void MainWindow::cut()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->cut();
    }
}

void MainWindow::copy()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->copy();
    }
}

void MainWindow::copyAsHtml()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);

        CodeEditor editorClone;
        editorClone.setFont(editor->font());
        editorClone.setTabStopDistance(editor->tabStopDistance());

        QTextCursor textCursorClone = editorClone.textCursor();
        QTextCursor textCursor = editor->textCursor();
        QString text = textCursor.selectedText();
        editorClone.setPlainText(text);

        QString plainText = editorClone.document()->toPlainText();
        static QRegularExpression re1("^\n+", QRegularExpression::CaseInsensitiveOption);
        static QRegularExpression re2("\\s+$", QRegularExpression::CaseInsensitiveOption);
        plainText = plainText.remove(re1); //去掉头部换行字符
        plainText = plainText.remove(re2); //去掉尾部空白字符
        plainText = plainText.replace(" ", "  "); //替换1个空格符为2个，后面置换2个空格符为1个html空格。
        plainText = plainText.replace("\n\n", "\n\x02\n"); //将两个连续换行符置换为换行符-02字符-换行符，避免html中出现问题
        editorClone.setPlainText(plainText);

        QTextBlock currentBlock = editorClone.document()->begin();
        while (currentBlock.isValid())
        {
            foreach (QTextLayout::FormatRange r, currentBlock.layout()->formats())
            {
                textCursorClone.setPosition(currentBlock.position() + r.start, QTextCursor::MoveAnchor);
                textCursorClone.setPosition(currentBlock.position() + r.start + r.length, QTextCursor::KeepAnchor);
                textCursorClone.mergeCharFormat(r.format);
            }
            currentBlock = currentBlock.next();
        }

        //转换为html文本，将文本中的\t制表符更换成&#9;，也就是html中的制表符
        QString htmlStr = editorClone.document()->toHtml().replace("\t", "&#9;");
        htmlStr = htmlStr.replace("  ", "&#32;"); //替换2个空格符为html中的1个空格&nbsp;或者&#32;(html普通空格)，避免空格被省略。

        QMimeData* mimeData = new QMimeData();
        mimeData->setHtml(htmlStr);
        clipboard->setMimeData(mimeData);
    }
}

void MainWindow::paste()
{
    if (mainTabWidget->count() < 1)
        addEditorTab(getUntitledName());

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->paste();
    }
}

void MainWindow::selectAll()
{
    if (mainTabWidget->count() < 1)
        return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->selectAll();
    }
}

void MainWindow::deleteSelection()
{
    if (mainTabWidget->count() < 1)
        return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->textCursor().removeSelectedText();
    }
}

void MainWindow::insertCMain()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->textCursor().insertText(CMainString);
    }
}

void MainWindow::insertCppMain()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        editor->textCursor().insertText(CPPMainString);
    }
}

void MainWindow::insertCppQtMain()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    if (widget)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor*>(widget);
        QString QtMainString =
            "#include <QtWidgets/QtWidgets>\n\n"
            "int main(int argc, char *argv[])\n"
            "{\n"
            "	QApplication::setStyle(QStyleFactory::create(\"fusion\"));\n"
            "	QApplication app(argc, argv);\n"
            "	app.setFont(QFont(QStringList{\"Arial\",\"Microsoft Yahei\"},11));\n"
            "	\n"
            "	QWidget widget;\n"
            "	widget.resize(720,480);\n"
            "	widget.show();\n"
            "	\n"
            "	app.exec();\n"
            "}";
        editor->textCursor().insertText(QtMainString);
    }
}

void MainWindow::indent()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
    editor->changeIndent("increase");
}

void MainWindow::unindent()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
    editor->changeIndent("decrease");
}

void MainWindow::commentOnOff()
{
    if (mainTabWidget->count() < 1) return;

    QWidget *widget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
    editor->setComment();
}

void MainWindow::formatOptionActionClicked()
{
    AStyleOptionDialog aStyleOptionDialog(this);

    if (aStyleOptionDialog.exec() == QDialog::Accepted)
        isAutoFormat = aStyleOptionDialog.autoFormatCheckBox->isChecked();
}

void MainWindow::formatFileActionClicked()
{
    if (mainTabWidget->count() < 1) return;

    QString command = QApplication::applicationDirPath() + "/astyle.exe";
    if (!QFileInfo::exists(command))
    {
        QMessageBox::warning(this, "AStyle nonexist!", tr("AStyle.exe does not exist!"));
        return;
    }

    QWidget *widget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
    //editor->setCenterOnScroll(true);//使得光标保持在窗口中心
    if (editor->document()->characterCount() < 2) return;

    //记住水平和垂直滚动条的位置，方便格式化以后恢复位置
    int vScrollValue = editor->verticalScrollBar()->value();
    int hScrollValue = editor->horizontalScrollBar()->value();

    QString tempFileName = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation) + "/AStyleTemporaryFile";
    QFile file(tempFileName);
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        QTextStream out(&file);
        out.setEncoding(QStringConverter::Utf8);
        out << editor->toPlainText();
        file.close();
    }

    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    int bracketStyleComboBoxCurrentIndex = setting.value("BracketStyle", 0).toInt();
    QString indentStyleComboBoxCurrentText = setting.value("IndentStyle", "tab").toString();

    int tabSpaceNumber = setting.value("AStyleIndentWidth", 4).toInt();
    if (tabSpaceNumber < 2) tabSpaceNumber = 2;
    else if (tabSpaceNumber > 20) tabSpaceNumber = 20;

    bool maxLineWidthCheckBoxIsChecked = setting.value("MaxLineWidthCheckBox", false).toBool();
    int maxLineWidthSpinBoxValue = setting.value("MaxLineWidthSpinBox", 120).toInt();

    bool classesCheckBoxIsChecked = setting.value("ClassesCheckBox", true).toBool();
    bool namespacesCheckBoxIsChecked = setting.value("NamespacesCheckBox", true).toBool();
    bool switchesCheckBoxIsChecked = setting.value("SwitchesCheckBox", true).toBool();
    bool labelsCheckBoxIsChecked = setting.value("LabelsCheckBox", true).toBool();
    bool casesCheckBoxIsChecked = setting.value("CasesCheckBox", true).toBool();
    bool commentsCheckBoxIsChecked = setting.value("CommentsCheckBox", true).toBool();
    bool deleteExtraLinesCheckBoxIsChecked = setting.value("DeleteExtraLinesCheckBox", true).toBool();
    bool insertSpaceCheckBoxIsChecked = setting.value("InsertSpaceCheckBox", true).toBool();

    QStringList arguments{tempFileName};
    arguments << "-n"; //不备份代码文件
    arguments << tempFileName << "-A" + QString::number(bracketStyleComboBoxCurrentIndex + 1); //大括号格式化风格
    arguments << "--indent=" + indentStyleComboBoxCurrentText + "=" + QString::number(tabSpaceNumber); //缩进风格

    if (maxLineWidthCheckBoxIsChecked)
        arguments << "--max-code-length=" + QString::number(maxLineWidthSpinBoxValue); //每行最多字符数
    if (classesCheckBoxIsChecked)
        arguments << "--indent-" + QString("classes");
    if (namespacesCheckBoxIsChecked)
        arguments << "--indent-" + QString("namespaces");
    if (switchesCheckBoxIsChecked)
        arguments << "--indent-" + QString("switches");
    if (labelsCheckBoxIsChecked)
        arguments << "--indent-" + QString("labels");
    if (casesCheckBoxIsChecked)
        arguments << "--indent-" + QString("cases");
    if (commentsCheckBoxIsChecked)
        arguments << "--indent-col1-" + QString("comments");
    if (deleteExtraLinesCheckBoxIsChecked)
        arguments << "--squeeze-lines=1"; //删除多于1行的空行
    if (insertSpaceCheckBoxIsChecked)
        arguments << "-p"; //运算符前后和逗号后插入空格

    QProcess process;
    process.start(command, arguments);
    process.waitForFinished();

    QByteArray error = process.readAllStandardError();
    QString errorStr = error;
    if (!error.isValidUtf8())
        errorStr = cn(error);

    if (!errorStr.isEmpty())
    {
        QMessageBox::warning(this, "Error in formatting", errorStr);
        return;
    }

    QFile formattedFile(tempFileName);
    bool success = formattedFile.open(QIODevice::ReadOnly | QIODevice::Text);
    if (!success) return;

    QString codeStr = formattedFile.readAll();
    formattedFile.close();

    QTextCursor textCursor = editor->textCursor();
    int blockNumber = textCursor.blockNumber(); //当前段落号
    int positionInBlock = textCursor.positionInBlock(); //光标在当前段落的位置

    textCursor.beginEditBlock();
    textCursor.select(QTextCursor::Document);
    textCursor.removeSelectedText();
    editor->appendPlainText(codeStr);//如果用setPlainText会导致documentTitle消失

    //按照段落号查询到格式化后的段落
    QTextBlock textBlock;
    if (blockNumber < editor->document()->blockCount())
        textBlock = editor->document()->findBlockByLineNumber(blockNumber);
    else
        textBlock = editor->document()->lastBlock();
    //格式化后光标应该的位置
    int newPosition = textBlock.position() + positionInBlock;
    if (newPosition >= editor->document()->characterCount())
        newPosition = editor->document()->characterCount() - 1;
    //恢复光标位置
    textCursor.setPosition(newPosition, QTextCursor::MoveAnchor);
    textCursor.insertText(" ");//插入一个空字符，然后删除，是为了能够redo到光标处。
    textCursor.deletePreviousChar();
    //恢复格式化前的滚动条位置
    editor->verticalScrollBar()->setValue(vScrollValue);
    editor->horizontalScrollBar()->setValue(hScrollValue);

    editor->setTextCursor(textCursor);
    textCursor.endEditBlock();

    //editor->setCenterOnScroll(false);//取消光标保持在窗口中心的功能，防止产生多余的滚动范围

    if (indentStyleComboBoxCurrentText != "spaces")
    {
        editor->setTabSpaceNumber(tabSpaceNumber);
        QFontMetrics fm(editor->font());
        double fontWidth = fm.horizontalAdvance("a");
        editor->setTabStopDistance(fontWidth * editor->getTabSpaceNumber());
    }

    QFile::remove(tempFileName);
}

void MainWindow::zoom()
{
    if (mainTabWidget->count() < 1) return;

    QAction *action = dynamic_cast<QAction*>(sender());

    QWidget *currentWidget = mainTabWidget->currentWidget();
    CodeEditor *editor = dynamic_cast<CodeEditor*>(currentWidget);

    if (action == zoomInAction)
        editor->zoomIn(1);
    else
        editor->zoomOut(1);

    QFontMetrics fm(editor->font());
    double fontWidth = fm.horizontalAdvance("a");
    editor->setTabStopDistance(fontWidth * editor->getTabSpaceNumber());

    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    setting.setValue("EditorFontSize", editor->font().pointSize());
}

void MainWindow::toolBarOnOff()
{
    if (toolBarAction->icon().isNull())
    {
        toolBarAction->setIcon(yesPixmap);
        mainToolBar->setVisible(true);
        toolBarDockMenu->setEnabled(true);
    }
    else
    {
        toolBarAction->setIcon(QPixmap());
        mainToolBar->setVisible(false);
        toolBarDockMenu->setEnabled(false);
    }
}

void MainWindow::statusBarOnOff()
{
    if (statusBarAction->icon().isNull())
    {
        statusBarAction->setIcon(yesPixmap);
        statusBar->setVisible(true);
    }
    else
    {
        statusBarAction->setIcon(QPixmap());
        statusBar->setVisible(false);
    }
}

void MainWindow::fileMenuAboutToShow()
{
    int historyCount=historyMenu->actions().count();
    if (historyCount>2) historyMenu->setEnabled(true);
    else historyMenu->setEnabled(false);
}

void MainWindow::projectMenuAboutToShow()
{
    int topLevelCount=treeWidget->topLevelItemCount();
    if (topLevelCount>0)
    {
        foreach (QAction *action, projectMenu->actions())
            action->setEnabled(true);

        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        QString projectFile=topItem->data(0, Qt::UserRole).toString();
        QString makefile=QFileInfo(projectFile).path()+"/Makefile.mk";
        if (QFileInfo::exists(makefile))
            makefileAction->setEnabled(true);
        else
            makefileAction->setEnabled(false);

        if (topItem->data(0, Qt::StatusTipRole).value<ProgramType>()==CProgram)
            addNewClassAction->setEnabled(false);
        else
            addNewClassAction->setEnabled(true);
    }
    else
    {
        foreach (QAction *action, projectMenu->actions())
            action->setEnabled(false);

        newProjectAction->setEnabled(true);
        openProjectAction->setEnabled(true);
    }
}

void MainWindow::projectContextMenuAboutToShow()
{
    foreach (QAction *action, projectContextMenu->actions())
        action->setEnabled(true);
    int topItemCount=treeWidget->topLevelItemCount();
    if (topItemCount>0)
    {
        QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
        QString projectFile=topItem->data(0, Qt::UserRole).toString();
        QString makefile=QFileInfo(projectFile).path()+"/Makefile.mk";
        if (QFileInfo::exists(makefile))
            makefileAction->setEnabled(true);
        else
            makefileAction->setEnabled(false);

        if (topItem->data(0, Qt::StatusTipRole).value<ProgramType>()==CProgram)
            addNewClassAction->setEnabled(false);
        else
            addNewClassAction->setEnabled(true);
    }
}

void MainWindow::itemContextMenuAboutToShow()
{
    foreach (QAction *action, itemContextMenu->actions())
        action->setEnabled(true);
}

void MainWindow::toolBarDockMenuAboutToShow()
{
    foreach (QAction* action, toolBarDockMenu->actions())
        action->setIcon(QPixmap());

    if (this->toolBarArea(mainToolBar) == Qt::BottomToolBarArea)
        dockBottomAction->setIcon(yesPixmap);
    else if (this->toolBarArea(mainToolBar) == Qt::LeftToolBarArea)
        dockLeftAction->setIcon(yesPixmap);
    else if (this->toolBarArea(mainToolBar) == Qt::RightToolBarArea)
        dockRightAction->setIcon(yesPixmap);
    else if (this->toolBarArea(mainToolBar) == Qt::TopToolBarArea)
        dockTopAction->setIcon(yesPixmap);
}

void MainWindow::newProject()
{
    //打开新项目对话框，设置新项目参数。
    NewProjectDialog newProjectDialog(this);
    if (newProjectDialog.exec()==QDialog::Accepted)
    {
        int topItemCount=treeWidget->topLevelItemCount();
        QList<QTreeWidgetItem*> topItemList;
        for (int i=0; i<topItemCount; i++)
            topItemList<<treeWidget->topLevelItem(i);
        //关闭现在所有的项目
        foreach (QTreeWidgetItem* topItem, topItemList)
        {
            QString projectFile=topItem->data(0,Qt::UserRole).toString();
            closeProject(topItem);
            insertRecentFileAction(projectFile);//添加文件名到历史列表
        }

        if (hSplitter->sizes().at(0) == 0)   //如果项目管理窗口处于隐藏状态，打开显示
        {
            QList<int> sizeList;
            sizeList << 1000 << 7000;
            hSplitter->setSizes(sizeList);
        }

        QString projectFolder=newProjectDialog.projectPath();
        QDir projectDir(projectFolder);

        QString projectMainFile=projectFolder+"/"+"main.cpp";
        ProgramType programType=CPPProgram;
        if (newProjectDialog.isCRadioButtonChecked())
        {
            programType=CProgram;
            projectMainFile=projectFolder+"/"+"main.c";
        }

        QStringList sourceList{projectMainFile};

        QFile file(projectMainFile); //file.write(str.toUtf8()); //这种形式更简洁。
        if (file.open(QIODevice::WriteOnly | QIODevice::Text))
        {
            QTextStream out(&file);

            QStringConverter::Encoding encoding = QStringConverter::System;
            if (encodingIndex != 0)
                encoding = QStringConverter::Utf8;

            out.setEncoding(encoding);

            out << (programType==CProgram? CMainString: CPPMainString);
            file.close();
        }
        else
        {
            QMessageBox::warning(this, tr("Failed to save main file!"), tr("Failed to save main source file!"));
        }

        //读取编译器列表信息和前次使用的编译器索引号
        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

        QList<CompilerSet> compilerSetList;
        compilerSetList = setting.value("CompilerSet").value<QList<CompilerSet>>();
        int compilerIndex = setting.value("CurrentCompilerIndex", -1).toInt();

        //确定编译器
        CompilerSet compilerSet;
        if (compilerSetList.count() > 0)
        {
            if (compilerIndex >= 0 && compilerIndex < compilerSetList.count())
                compilerSet = compilerSetList[compilerIndex];
            else
                compilerSet = compilerSetList[0];
        }

        QString projectFile=projectFolder + "/"+projectDir.dirName()+".project";
        setupProject(projectFile, sourceList, QStringList{}, programType, compilerSet, encodingIndex);
    }
}

void MainWindow::openProject(QString projectFile)
{
    if (hSplitter->sizes().at(0) == 0)   //如果项目管理窗口处于隐藏状态，打开显示
    {
        QList<int> sizeList;
        sizeList << 1000 << 7000;
        hSplitter->setSizes(sizeList);
    }

    int topItemCount=treeWidget->topLevelItemCount();
    for (int i=0;i<topItemCount; i++)//检测项目是否已经打开，如果已经打开，返回。
    {
        QTreeWidgetItem *topLevelItem=treeWidget->topLevelItem(i);
        if (QFileInfo(topLevelItem->data(0, Qt::UserRole).toString()).absoluteFilePath()==QFileInfo(projectFile).absoluteFilePath())
        {
            QMessageBox::warning(this, tr("Project already opened!"), tr("%1The above project already opened!").arg(projectFile+"\n\n"));
            updateRecentFileActionList(projectFile);
            return;
        }
    }

    //确认是否关闭现有所有项目
    QList<QTreeWidgetItem*> topItemList;
    for (int i=0; i<topItemCount; i++)
        topItemList<<treeWidget->topLevelItem(i);
    //关闭现在所有的项目
    foreach (QTreeWidgetItem* topItem, topItemList)
    {
        QString projectFile=topItem->data(0,Qt::UserRole).toString();
        closeProject(topItem);
        insertRecentFileAction(projectFile);//添加文件名到历史列表
    }

    QSettings setting(projectFile, QSettings::IniFormat);
    QString projectName = setting.value("ProjectName","").toString();
    ProgramType programType = setting.value("ProgramType", ProgramType::CProgram).value<ProgramType>();
    QStringList sourceFileList = setting.value("SourceFiles").value<QStringList>();
    QStringList precompileFiles = setting.value("PrecompileFiles").value<QStringList>();
    CompilerSet compilerSet = setting.value("CompilerSet").value<CompilerSet>();
    bool ok=false;
    int fileEncodingIndex = setting.value("FileEncodingIndex",-1).toInt(&ok);
    if (!ok) fileEncodingIndex=-1;

    if (projectName.isEmpty())
    {
        QMessageBox::warning(this, tr("Loading project error!"), tr("%1Failed to load project from the above file!").arg(projectFile+"\n\n"));
        updateRecentFileActionList(projectFile);
        return;
    }

    //将源代码文件的相对路径名换成绝对路径名
    QStringList absoluteSourceFileList;
    QDir projectDir(QFileInfo(projectFile).path());
    foreach (QString fileName, sourceFileList)
    {
        QFileInfo fileInfo(fileName);
        QString absoluteFileName=fileName;
        if (fileInfo.isRelative())
            absoluteFileName=projectDir.absoluteFilePath(fileName);

        absoluteSourceFileList<<absoluteFileName;
    }

    QStringList absolutePrecompileFileList;
    foreach (QString fileName, precompileFiles)
    {
        QFileInfo fileInfo(fileName);
        QString absoluteFileName=fileName;
        if (fileInfo.isRelative())
            absoluteFileName=projectDir.absoluteFilePath(fileName);

        absolutePrecompileFileList<<absoluteFileName;
    }

    setupProject(projectFile, absoluteSourceFileList, absolutePrecompileFileList, programType, compilerSet, fileEncodingIndex);

    updateRecentFileActionList(projectFile);

    //记录当前打开的路径作为下次打开时的路径
    QString filePath = QFileInfo(projectFile).path();
    if (!filePath.isEmpty())
    {
        QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
        QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);
        setting.setValue("LastFilePath", filePath); //记录当前路径到QSetting中保存
    }
}

void MainWindow::setupProject(QString projectFile, QStringList sourceList, QStringList precompileList, ProgramType programType, CompilerSet compilerSet, int fileEncodingIndex)
{
    QFileInfo projectFileInfo(projectFile);
    QTreeWidgetItem *topItem=new QTreeWidgetItem(treeWidget);
    topItem->setSizeHint(0, QSize(0,treeItemHeight));
    topItem->setText(0, projectFileInfo.completeBaseName());
    topItem->setIcon(0, QPixmap(":/images/topItem.png"));
    topItem->setData(0, Qt::UserRole, projectFile);
    QVariant variant;
    variant.setValue(compilerSet);
    topItem->setData(0, Qt::WhatsThisRole, variant);
    topItem->setData(0, Qt::AccessibleTextRole, fileEncodingIndex);

    if (programType==CProgram)
        topItem->setData(0, Qt::StatusTipRole, ProgramType::CProgram);
    else
        topItem->setData(0, Qt::StatusTipRole, ProgramType::CPPProgram);

    treeWidget->addTopLevelItem(topItem);

    foreach (QString fileName, sourceList)
    {
        if (!QFileInfo::exists(fileName))
        {
            QMessageBox::warning(this, tr("File Invalid!"), tr("%1The above file is not found!").arg(fileName+"\n\n"));
            continue;
        }

        addSubItem(topItem, fileName);
    }

    treeWidget->expandAll();
    treeWidget->sortItems(0, Qt::DescendingOrder);

    //查询预编译头文件，设定标记和专属图标
    int childCount=topItem->childCount();
    foreach (QString precompileName, precompileList)
    {
        for(int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *item=topItem->child(i);
            QString fileName=item->data(0, Qt::UserRole).toString();
            if (fileName.compare(precompileName, Qt::CaseInsensitive)==0)
            {
                item->setData(0, Qt::WhatsThisRole, "Precompile");
                item->setIcon(0, QPixmap(":/images/precompile.png"));
            }
        }
    }

    //如果有未改动的Untitled1页，移除。
    if (mainTabWidget->count() == 1 && mainTabWidget->tabText(0) == "Untitled1")
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        if (!QFileInfo::exists(editor->documentTitle())&&!editor->document()->isUndoAvailable() && !editor->document()->isRedoAvailable() && editor->toPlainText() == "")
            mainTabWidget->removeTab(0);//如果Untitled1页没有更改过，则删除。
    }

    //创建Tab页打开所有源文件。
    foreach (QString str, sourceList)
    {
        //if (str.endsWith("main.c", Qt::CaseInsensitive)||str.endsWith("main.cpp", Qt::CaseInsensitive))
            openSpecifiedFile(str);
    }

    //如果有main.c或者mian.cpp文件，设定为当前tab页
    for (int i=0; i<mainTabWidget->count(); i++)
    {
        QString tabText=mainTabWidget->tabText(i);
        if (tabText.compare("main.c", Qt::CaseInsensitive)==0||tabText.compare("main.cpp", Qt::CaseInsensitive)==0)
        {
            mainTabWidget->setCurrentIndex(i);
        }
    }
}

void MainWindow::addSubItem(QTreeWidgetItem *topItem, QString fileName)
{
    QFileInfo info(fileName);
    QTreeWidgetItem *item=new QTreeWidgetItem(topItem);
    item->setSizeHint(0, QSize(0, treeItemHeight));
    item->setText(0, info.fileName());

    QString projectFilename=topItem->data(0, Qt::UserRole).toString();
    QFileInfo projectFileInfo(projectFilename);

    QDir dir(projectFileInfo.path());
    if (info.isRelative())
        fileName = dir.absoluteFilePath(fileName);//如果是相对路径，获取绝对路径。

    item->setData(0, Qt::UserRole, fileName);

    if (fileName.endsWith(".c", Qt::CaseInsensitive))
        item->setIcon(0, QPixmap(":/images/CFile.png"));
    else if (fileName.endsWith(".cpp", Qt::CaseInsensitive)||fileName.endsWith(".cc", Qt::CaseInsensitive)||fileName.endsWith(".cxx", Qt::CaseInsensitive))
        item->setIcon(0, QPixmap(":/images/CPPFile.png"));
    else if (fileName.endsWith(".h", Qt::CaseInsensitive)||fileName.endsWith(".hxx", Qt::CaseInsensitive)||fileName.endsWith(".hpp", Qt::CaseInsensitive))
        item->setIcon(0, QPixmap(":/images/HeaderFile.png"));
    else if (fileName.endsWith(".rc", Qt::CaseInsensitive))
        item->setIcon(0, QPixmap(":/images/ResourceFile.png"));
    else
        item->setIcon(0, QPixmap(":/images/question.png"));
}

void MainWindow::treeWidgetItemPressed(QTreeWidgetItem* item, int column)
{
    if(qApp->mouseButtons() == Qt::RightButton) // 只针对鼠标右键
    {
        // 右键菜单在鼠标点击的位置显示
        if (item->parent()==nullptr)//如果是top level item，parent()是nullptr
            projectContextMenu->exec(QCursor::pos());
        else
        {
            QString fileName=item->data(0, Qt::UserRole).toString();
            if (fileName.endsWith(".h", Qt::CaseInsensitive)||fileName.endsWith(".hxx", Qt::CaseInsensitive)||fileName.endsWith(".hpp", Qt::CaseInsensitive))
            {
                precompileAction->setVisible(true);
                QString precompileMark=item->data(0, Qt::WhatsThisRole).toString();
                if (precompileMark=="Precompile")
                    precompileAction->setText(tr("Precompile OFF"));
                else
                    precompileAction->setText(tr("Precompile ON"));
            }
            else
            {
                precompileAction->setVisible(false);
            }

            itemContextMenu->exec(QCursor::pos());
        }
    }
}

void MainWindow::addNewProjectFile()
{
    QTreeWidgetItem *currentTopItem=treeWidget->currentItem();
    QString projectName=currentTopItem->data(0, Qt::UserRole).toString();
    bool ok=false;
    QString defaultName=currentTopItem->data(0, Qt::StatusTipRole).value<ProgramType>()==CProgram? "Untitled.c":"Untitled.cpp";
    QString newSourceName=QInputDialog::getText(this, tr("New source file name"),tr("Please input new source file name:"), QLineEdit::Normal, defaultName, &ok );
    if (ok)
    {
        newSourceName=newSourceName.remove("/");
        newSourceName=newSourceName.remove("\\");
        newSourceName=newSourceName.trimmed();

        if (newSourceName.contains(" "))
        {
            QMessageBox::warning(this, "Warning!", tr("Failed to add new file! White spaces are not allowed in source filename."));
            return;
        }

        QString newFileName=QFileInfo(projectName).path()+"/"+newSourceName;
        QFileInfo newFileInfo(newFileName);
        QFileInfoList dirInfoList = QDir(QFileInfo(projectName).path()).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
        if (dirInfoList.contains(newFileInfo))
        {
            QMessageBox::warning(this, tr("Duplicate file name!"), tr("%1The above file already exists, please input a different name!").arg(newFileName+"\n\n"));
            return;
        }

        QFile file(newFileName);
        bool success = file.open(QIODevice::ReadWrite | QIODevice::Text);
        if (!success)
        {
            QMessageBox::warning(this, "Warning!", tr("%1Failed to create the above file! Filename contains illegal characters!").arg(newFileName+"\n\n"));
            return;
        }
        file.close();

        addSubItem(currentTopItem, newFileName);
        openSpecifiedFile(newFileName);

        if (treeWidget->topLevelItemCount()>0)
        {
            treeWidget->expandAll();
            treeWidget->sortItems(0, Qt::DescendingOrder);
        }

        if (!isCodeFileSuffix(newFileName))
            QMessageBox::warning(this, tr("Not code file suffix"), tr("Only *.c, *.cpp, *.cc, *.cxx, *.h, *.hxx, *.hpp, *.rc files can be compiled!"));
    }
}

void MainWindow::addNewProjectClass()
{
    QTreeWidgetItem *currentTopItem=treeWidget->currentItem();
    QString projectName=currentTopItem->data(0, Qt::UserRole).toString();
    bool ok=false;
    QString defaultName="Untitled";
    QString newClassName=QInputDialog::getText(this, tr("New class name"),tr("Input new class name:"), QLineEdit::Normal, defaultName, &ok );
    if (ok)
    {
        newClassName=newClassName.remove("/");
        newClassName=newClassName.remove("\\");
        newClassName=newClassName.trimmed();

        if (newClassName.contains(" "))
        {
            QMessageBox::warning(this, tr("Warning!"), tr("Failed to add new class! White spaces are not allowed in class name!"));
            return;
        }

        QString newHeaderName=QFileInfo(projectName).path()+"/"+newClassName+".h";
        QString sourceSuffix=currentTopItem->data(0, Qt::StatusTipRole).value<ProgramType>()==CProgram?".c":".cpp";
        QString newSourceName=QFileInfo(projectName).path()+"/"+newClassName+sourceSuffix;

        QFileInfo newHeaderInfo(newHeaderName);
        QFileInfo newSourceInfo(newSourceName);
        QFileInfoList dirInfoList = QDir(QFileInfo(projectName).path()).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
        if (dirInfoList.contains(newHeaderInfo)||dirInfoList.contains(newSourceInfo))
        {
            QMessageBox::warning(this, tr("Duplicate class name!"), tr("%1The above class or file already exists!%2Please input a different class name!").arg(newClassName+"\n\n", "\n"));
            return;
        }

        QFile headerFile(newHeaderName);
        QFile sourceFile(newSourceName);
        bool headerSuccess = headerFile.open(QIODevice::ReadWrite | QIODevice::Text);
        bool sourceSuccess = sourceFile.open(QIODevice::ReadWrite | QIODevice::Text);
        if (!headerSuccess||!sourceSuccess)
        {
            QMessageBox::warning(this, "Warning!", tr("%1Failed to create the above class! Maybe classname contains illegal characters!").arg(newClassName+"\n\n"));
            return;
        }

        QString headerFileCode= "#ifndef "+newClassName.toUpper()+"_H\n"
                                "#define "+newClassName.toUpper()+"_H\n\n"
                                "class "+newClassName+"\n"
                                "{\n"
                                "\tpublic:\n"
                                "\t\t"+newClassName+"();\n\n"
                                "\tprivate:\n\n"
                                "};\n\n"
                                "#endif";

        headerFile.write(headerFileCode.toUtf8());

        QString sourceFileCode="#include \""+newClassName+".h\"\n\n"+
                                newClassName+"::"+newClassName+"()\n"
                                "{\n\n}";

        sourceFile.write(sourceFileCode.toUtf8());

        headerFile.close();
        sourceFile.close();

        addSubItem(currentTopItem, newHeaderName);
        openSpecifiedFile(newHeaderName);
        addSubItem(currentTopItem, newSourceName);
        openSpecifiedFile(newSourceName);

        if (treeWidget->topLevelItemCount()>0)
        {
            treeWidget->expandAll();
            treeWidget->sortItems(0, Qt::DescendingOrder);
        }
    }
}

void MainWindow::addExistingFileToProject()
{
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    QString lastFilePath = setting.value("LastFilePath","").toString(); //获取上次的打开路径，如果获取不到，取缺省值为空字符串。
    if (lastFilePath.isEmpty()) lastFilePath=QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);//如果上次路径为空，则打开文档目录

    QTreeWidgetItem *currentTopItem=treeWidget->currentItem();
    QStringList newFileNameList = QFileDialog::getOpenFileNames(this, "Open File", lastFilePath, "Code files (*.c;*.cpp;*.cc;*.cxx;*.h;*.hxx;*.hpp;*.rc);;"
                                                                    "C source files(*.c);;C++ source files(*.cpp;*.cc;*.cxx);;Header files(*.h;*.hxx;*.hpp);;"
                                                                    "Resource files(*.rc);;Text file(*.txt);;All files(*.*)");
    if (newFileNameList.length()==0) return;

    //记录当前打开的路径作为下次打开时的路径
    QString filePath = QFileInfo(newFileNameList[0]).path();
    if (!filePath.isEmpty())
        setting.setValue("LastFilePath", filePath); //记录当前路径到QSetting中保存

    foreach (QString newFileName, newFileNameList)
    {
        int childCount=currentTopItem->childCount();
        bool fileExist=false;
        for (int i=0; i<childCount; i++)
        {
            QTreeWidgetItem *item=currentTopItem->child(i);
            if (QFileInfo(newFileName).absoluteFilePath()==QFileInfo(item->data(0, Qt::UserRole).toString()).absoluteFilePath())
            {
                QMessageBox::warning(this, tr("File already exist!"), tr("%1The above file already exists in current project!").arg(newFileName+"\n\n"));
                fileExist=true;
                break;
            }
        }
        if (fileExist) continue;

        if (newFileName.endsWith(".project",Qt::CaseInsensitive))
        {
            QMessageBox::information(this, "Attention", "*.project file can not be added into project!");
            continue;
        }

        addSubItem(currentTopItem, newFileName);
        openSpecifiedFile(newFileName);

        //对新打开的文件进行读取并按照当前的文件编码格式重新保存。
        bool success=false;
        QString source=readStringFromFile(newFileName, success);
        if (success&&!source.isEmpty())
            save(source, newFileName);

        if (!isCodeFileSuffix(newFileName))
            QMessageBox::warning(this, tr("Not code file suffix"), tr("Only *.c, *.cpp, *.cc, *.cxx, *.h, *.hxx, *.hpp, *.rc files can be compiled!"));
    }

    if (treeWidget->topLevelItemCount()>0)
    {
        treeWidget->expandAll();
        treeWidget->sortItems(0, Qt::DescendingOrder);
    }
}

bool MainWindow::isCodeFileSuffix(QString fileName)
{
    QString suffix=QFileInfo(fileName).suffix();
    if (codeFileSuffixList.contains(suffix, Qt::CaseInsensitive))
        return true;
    else
        return false;
}

void MainWindow::clearTempFileActionClicked()
{
    clearTempFiles(QStringList{"*.o", "*.res", "*.gch"});
}

void MainWindow::clearTempFiles(QStringList filters)
{
    int topItemCount=treeWidget->topLevelItemCount();
    if (topItemCount==0) return;

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    QString projectPath=QFileInfo(topItem->data(0, Qt::UserRole).toString()).path();
    QDir dir(projectPath);
    dir.setNameFilters(filters);
    QFileInfoList dirInfoList = dir.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
    foreach (QFileInfo info, dirInfoList)
        QFile::remove(info.absoluteFilePath());
}

void MainWindow::projectInformation()
{
    int topItemCount=treeWidget->topLevelItemCount();
    if (topItemCount==0) return;

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    QString projectFile=topItem->data(0, Qt::UserRole).toString();
    QString projectPath=QFileInfo(projectFile).path();
    QString projectType=tr("C++ Project");
    if (topItem->data(0, Qt::StatusTipRole).value<ProgramType>()==CProgram)
        projectType=tr("C Project");

    QString resourceFiles;
    QString precompileFiles;
    int childCount=topItem->childCount();
    for (int i=0; i<childCount; i++)
    {
        QString fileName=topItem->child(i)->text(0);
        if (fileName.endsWith(".rc"))
            resourceFiles+=(fileName+"  ");
        if (topItem->child(i)->data(0, Qt::WhatsThisRole).toString()=="Precompile")
            precompileFiles+=(fileName+"  ");
    }

    if (resourceFiles.isEmpty()) resourceFiles=tr("None");
    if (precompileFiles.isEmpty()) precompileFiles=tr("None");

    InformationDialog infoDialog(this);
    QTableWidget *tableWidget=infoDialog.getTableWidget();

    int rowNum=0;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Project File")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(QFileInfo(projectFile).fileName()));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Project Path")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(projectPath));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Project Type")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(projectType));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Makefile")));
    QString makefileName=projectPath+"/Makefile.mk";
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(QFile::exists(makefileName)?QFileInfo(makefileName).fileName():tr("None")));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Resource File (*.rc)")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(resourceFiles.trimmed()));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Precompile Header")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(precompileFiles.trimmed()));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Number of Files")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(QString::number(topItem->childCount())));

    rowNum++;
    tableWidget->insertRow(rowNum);
    tableWidget->setItem(rowNum,0,new QTableWidgetItem(tr("Total Lines Number")));
    tableWidget->setItem(rowNum,1,new QTableWidgetItem(QString::number(getProjectTotalLinesNumber(topItem))));

    rowNum++;
    tableWidget->resizeRowsToContents();
    int tableWidgetHeigh=0;
    for (int i=0; i<rowNum; i++)
        tableWidgetHeigh+=tableWidget->rowHeight(i);

    infoDialog.setFixedSize(QSize(580, tableWidgetHeigh+80));
    infoDialog.exec();
}

int MainWindow::getProjectTotalLinesNumber(QTreeWidgetItem* item)
{
    int childItemCount=item->childCount();
    int linesNumber=0;
    for (int i=0; i<childItemCount; i++)
    {
        QString fileName=item->child(i)->data(0,Qt::UserRole).toString();
        bool success=false;
        QString source=readStringFromFile(fileName, success);
        if (!source.isEmpty())
        {
            QStringList sourceLines=source.split('\n');
            linesNumber+=sourceLines.size();
        }
    }

    return linesNumber;
}

void MainWindow::closeProjectMenuClicked()
{
    int topItemCount=treeWidget->topLevelItemCount();
    QList<QTreeWidgetItem*> topItemList;
    for (int i=0; i<topItemCount; i++)
        topItemList<<treeWidget->topLevelItem(i);

    foreach (QTreeWidgetItem *item, topItemList)
    {
        QString projectFile=item->data(0,Qt::UserRole).toString();
        closeProject(item);
        insertRecentFileAction(projectFile);//添加文件名到历史列表
    }
}

void MainWindow::closeProject(QTreeWidgetItem *currentTopItem)
{
    QString projectName=currentTopItem->data(0, Qt::UserRole).toString();
    QDir projectDir(QFileInfo(projectName).path());
    //获得项目的所有文件全名列表
    int count = currentTopItem->childCount();
    QStringList itemFileList;
    for (int i=0; i<count; i++)
    {
        itemFileList<<QFileInfo(currentTopItem->child(i)->data(0, Qt::UserRole).toString()).absoluteFilePath();
    }

    //获得项目中已经打开的文件tab页
    QList<CodeEditor*> projectCodeEditorList;
    int tabCount=mainTabWidget->count();
    for (int i=0; i<tabCount; i++)
    {
        QWidget * widget=mainTabWidget->widget(i);
        CodeEditor *codeEditor=dynamic_cast<CodeEditor *>(widget);
        if (itemFileList.contains(QFileInfo(codeEditor->documentTitle()).absoluteFilePath(), Qt::CaseInsensitive))
            projectCodeEditorList<<codeEditor;
    }

    //如果有修改的文件，询问是否保存，然后销毁该tab页。
    foreach (CodeEditor *editor, projectCodeEditorList)
    {
        if (editor->document()->isModified())
        {
            mainTabWidget->setCurrentWidget(editor);
            QMessageBox::StandardButton result = QMessageBox::question(this, tr("Confirm"), tr("%1 project will be closed, save changes to %2?").arg(QFileInfo(projectName).completeBaseName(), projectDir.relativeFilePath(editor->documentTitle())), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
            if (result == QMessageBox::Yes)
                saveFile(editor);
        }

        mainTabWidget->removeTab(mainTabWidget->indexOf(editor));
        delete editor;
    }

    QSettings setting(projectName, QSettings::IniFormat);
    setting.setValue("ProjectName", QFileInfo(projectName).completeBaseName());
    setting.setValue("ProgramType", currentTopItem->data(0, Qt::StatusTipRole));
    QVariant variant;
    variant.setValue(currentTopItem->data(0, Qt::WhatsThisRole).value<CompilerSet>());
    setting.setValue("CompilerSet", variant);
    setting.setValue("FileEncodingIndex", currentTopItem->data(0, Qt::AccessibleTextRole));

    int childCount=currentTopItem->childCount();
    QStringList sourceList;
    QStringList precompileList;
    for (int i=0; i<childCount; i++)
    {
        QTreeWidgetItem *item=currentTopItem->child(i);
        QString fileName=item->data(0, Qt::UserRole).toString();
        QString relativeFileName=projectDir.relativeFilePath(fileName);
        sourceList<<(relativeFileName.contains("..") ? fileName : relativeFileName);
        if (fileName.endsWith(".h", Qt::CaseInsensitive)||fileName.endsWith(".hxx", Qt::CaseInsensitive)||fileName.endsWith(".hpp", Qt::CaseInsensitive))
        {
            if (item->data(0, Qt::WhatsThisRole).toString()=="Precompile")
                precompileList<<(relativeFileName.contains("..") ? fileName : relativeFileName);
        }
    }
    setting.setValue("SourceFiles", sourceList);
    setting.setValue("PrecompileFiles", precompileList);

    insertRecentFileAction(projectName);//保存到历史清单中

    delete currentTopItem;//删除选中的顶层item
}

void MainWindow::openExplorer()
{
    QTreeWidgetItem *currentItem=treeWidget->currentItem();
    QString fileName=currentItem->data(0, Qt::UserRole).toString();

    QProcess *process = new QProcess(this);

    QFileInfo info(fileName);
    if (!info.exists())
    {
        process->start("explorer.exe");
    }
    else
    {
        QStringList args;
        args << "/select," << QDir::toNativeSeparators(fileName);
        process->start("explorer.exe", args);
    }
}

void MainWindow::precompileOnOff()
{
    QTreeWidgetItem *currentItem=treeWidget->currentItem();
    QString precompileMark=currentItem->data(0, Qt::WhatsThisRole).toString();
    if (precompileMark=="Precompile")
    {
        currentItem->setData(0, Qt::WhatsThisRole, "");
        currentItem->setIcon(0, QPixmap(":/images/HeaderFile.png"));
        QString headerFileName=currentItem->data(0, Qt::UserRole).toString();
        QFileInfo headerInfo(headerFileName);
        QString gchName=headerInfo.path()+"/"+headerInfo.fileName()+".gch";
        QFile::remove(gchName);
    }
    else
    {
        currentItem->setData(0, Qt::WhatsThisRole, "Precompile");
        currentItem->setIcon(0, QPixmap(":/images/precompile.png"));
    }
}

void MainWindow::renameProjectFile()
{
    QTreeWidgetItem *currentItem=treeWidget->currentItem();
    QString fileName=currentItem->data(0, Qt::UserRole).toString();

    bool ok=false;
    QString defaultName=currentItem->text(0);
    QString newName=QInputDialog::getText(this, tr("New file name"),tr("Input a new file name:"), QLineEdit::Normal, defaultName, &ok );
    if (ok)
    {
        //正则表达式清除字符串中的文件名非法字符，共九个。
        static QRegularExpression re("[\\\\/:*?\"<>|]");
        newName=newName.remove(re);
        newName=newName.trimmed();

        if (newName.contains(" "))
        {
            QMessageBox::warning(this, tr("Rename failed!"), tr("Rename failed! White spaces are not allowed in filename!"));
            return;
        }

        QString newFileName=QFileInfo(fileName).path()+"/"+newName;
        QFileInfo newFileInfo(newFileName);
        QFileInfoList dirInfoList = QDir(QFileInfo(fileName).path()).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
        if (dirInfoList.contains(newFileInfo))
        {
            QMessageBox::warning(this, tr("Duplicate file name!"), tr("%1The above file already exists, please input a different name!").arg(newFileName+"\n\n"));
            return;
        }

        bool renameOK=QFile::rename(fileName, newFileName);
        if (renameOK)
        {
            //如果是预编译头文件，并且存在预编译好的gch大文件，则删除。
            if (QFileInfo::exists(fileName+".gch"))
                QFile::remove(fileName+".gch");

            QFileInfo fileInfo(fileName);
            QString objectFileName=fileInfo.path()+"/"+fileInfo.completeBaseName()+".o";
            if (QFileInfo::exists(objectFileName))
                QFile::remove(objectFileName);

            currentItem->setText(0, newName);
            currentItem->setData(0, Qt::UserRole, newFileName);
            //改名的文件如果已经打开，tab页修改成新名。
            int tabCount=mainTabWidget->count();
            for (int i=0; i<tabCount; i++)
            {
                QWidget * widget=mainTabWidget->widget(i);
                CodeEditor *codeEditor=dynamic_cast<CodeEditor *>(widget);
                if (QFileInfo(codeEditor->documentTitle()).absoluteFilePath()==QFileInfo(fileName).absoluteFilePath())
                {
                    codeEditor->setDocumentTitle(newFileName);
                    mainTabWidget->setTabText(i, newName);
                }
            }

            //根据新后缀名修改图标
            QString fileNameSuffix=QFileInfo(fileName).suffix();
            QString newFileNameSuffix=QFileInfo(newFileName).suffix();
            if (fileNameSuffix.compare(newFileNameSuffix, Qt::CaseInsensitive)!=0)
            {
                if (newFileName.endsWith(".c", Qt::CaseInsensitive))
                    currentItem->setIcon(0, QPixmap(":/images/CFile.png"));
                else if (newFileName.endsWith(".cpp", Qt::CaseInsensitive)||newFileName.endsWith(".cc", Qt::CaseInsensitive)||newFileName.endsWith(".cxx", Qt::CaseInsensitive))
                    currentItem->setIcon(0, QPixmap(":/images/CPPFile.png"));
                else if (newFileName.endsWith(".h", Qt::CaseInsensitive)||newFileName.endsWith(".hxx", Qt::CaseInsensitive)||newFileName.endsWith(".hpp", Qt::CaseInsensitive))
                    currentItem->setIcon(0, QPixmap(":/images/HeaderFile.png"));
                else if (newFileName.endsWith(".rc", Qt::CaseInsensitive))
                    currentItem->setIcon(0, QPixmap(":/images/ResourceFile.png"));
                else
                    currentItem->setIcon(0, QPixmap(":/images/question.png"));

                if (newFileNameSuffix.compare("h", Qt::CaseInsensitive)!=0&&newFileNameSuffix.compare("hxx", Qt::CaseInsensitive)!=0&&newFileNameSuffix.compare("hpp", Qt::CaseInsensitive)!=0)
                        currentItem->setData(0, Qt::WhatsThisRole,"");

                if (newFileNameSuffix.compare("h", Qt::CaseInsensitive)==0||newFileNameSuffix.compare("hxx", Qt::CaseInsensitive)==0||newFileNameSuffix.compare("hpp", Qt::CaseInsensitive)==0)
                {
                    if (currentItem->data(0, Qt::WhatsThisRole).toString()=="Precompile")
                        currentItem->setIcon(0, QPixmap(":/images/precompile.png"));
                }
            }
        }
        else
        {
            QMessageBox::warning(this, tr("Rename failed!"), tr("Rename failed! Please input a suitable filename."));
        }
    }
}

void MainWindow::renameProject()
{
    int topItemCount=treeWidget->topLevelItemCount();
    if (topItemCount==0) return;

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    QString projectFile=topItem->data(0, Qt::UserRole).toString();
    QFileInfo projectFileInfo=QFileInfo(projectFile);

    bool ok=false;
    QString defaultName=topItem->text(0);
    QString newName=QInputDialog::getText(this, tr("New project name"),tr("Input a new project name:"), QLineEdit::Normal, defaultName, &ok );
    if (ok)
    {
        //正则表达式清除字符串中的文件名非法字符，共九个。
        static QRegularExpression re("[\\\\/:*?\"<>|]");
        newName=newName.remove(re);
        newName=newName.trimmed();

        if (newName.contains(" "))
        {
            QMessageBox::warning(this, tr("Rename failed!"), tr("Rename failed! White spaces are not allowed in project name!"));
            return;
        }

        QString newFileName=QFileInfo(projectFile).path()+"/"+newName+".project";
        QFileInfo newFileInfo(newFileName);
        QFileInfoList dirInfoList = QDir(QFileInfo(projectFile).path()).entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
        if (dirInfoList.contains(newFileInfo))
        {
            QMessageBox::warning(this, tr("Duplicate file name!"), tr("%1The above file already exists, please input a different name!").arg(newFileName+"\n\n"));
            return;
        }

        bool renameOK=QFile::rename(projectFile, newFileName);
        if (renameOK)
        {
            topItem->setText(0, QFileInfo(newName).completeBaseName());
            topItem->setData(0, Qt::UserRole, newFileName);
            QString exeFile=projectFileInfo.path()+"/"+projectFileInfo.completeBaseName()+".exe";
            if (QFileInfo::exists(exeFile))//如果原项目名存在关联的可执行文件，删除。
                QFile::remove(exeFile);
        }
        else
        {
            QMessageBox::warning(this, tr("Rename failed!"), tr("Rename failed! Please input a suitable project name."));
        }
    }
}

void MainWindow::removeProjectFile()
{
    // //通过鼠标位置获取控件的方式
    // QPoint newPos=treeWidget->mapFromGlobal(itemContextMenu.pos());
    // int y=treeWidget->viewport()->y();
    // int x=treeWidget->viewport()->x();
    // QTreeWidgetItem* item = treeWidget->itemAt(QPoint(newPos.x()-x, newPos.y()-y));

    QTreeWidgetItem *currentItem=treeWidget->currentItem();
    QString fileName=currentItem->data(0, Qt::UserRole).toString();

    QMessageBox msgBox(this);
    msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
    msgBox.setDefaultButton(QMessageBox::Yes);
    QCheckBox *checkbox = new QCheckBox(tr("Delete file completely"));
    msgBox.setCheckBox(checkbox);
    msgBox.setWindowTitle("Remove file");
    msgBox.setText(tr("%1Are you sure to remove the above file from project?").arg(fileName+"\n\n"));
    msgBox.setIcon(QMessageBox::Question);
    if (msgBox.exec()==QMessageBox::Yes)
    {
        //检测是否有修改，确认是否保存。
        int count = mainTabWidget->count();
        QWidget* selectedWidget=nullptr;
        for (int i=0; i<count; i++)
        {
            QWidget *widget = mainTabWidget->widget(i);
            CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
            if (QFileInfo(editor->documentTitle()).absoluteFilePath()==QFileInfo(fileName).absoluteFilePath())
            {
                selectedWidget=widget;
                if (editor->document()->isModified())
                {
                    mainTabWidget->setCurrentWidget(widget);
                    QMessageBox::StandardButton result = QMessageBox::question(this, tr("Confirm"), tr("Save changes to %1?").arg(editor->documentTitle()), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
                    if (result == QMessageBox::Yes)
                        saveFile(editor);
                }
            }
        }

        if (selectedWidget)
        {
            mainTabWidget->removeTab(mainTabWidget->indexOf(selectedWidget));
            delete selectedWidget;
        }

        delete currentItem;//删除选中的item
        if (checkbox->isChecked())
            QFile::moveToTrash(fileName);
    }
}

void MainWindow::treeWidgetItemDoubleClicked(QTreeWidgetItem *item, int column)
{
    if (item->parent()==nullptr) return;//如果点击的是顶层项目名，返回。

    QString fileName=item->data(0, Qt::UserRole).toString();
    openSpecifiedFile(fileName);//打开fileName文件读取内容，显示在新的tab页上。
}

void MainWindow::changeToolBarDock()
{
    QAction* clickedAction = dynamic_cast<QAction*>(sender());

    foreach (QAction* action, toolBarDockMenu->actions())
        action->setChecked(false);

    if (clickedAction == dockBottomAction)
        this->addToolBar(Qt::BottomToolBarArea, mainToolBar);
    else if (clickedAction == dockLeftAction)
        this->addToolBar(Qt::LeftToolBarArea, mainToolBar);
    else if (clickedAction == dockRightAction)
        this->addToolBar(Qt::RightToolBarArea, mainToolBar);
    else
        this->addToolBar(Qt::TopToolBarArea, mainToolBar);
}

void MainWindow::changeColorTheme()
{
    QAction* action = dynamic_cast<QAction*>(sender());
    lightThemeAction->setIcon(QPixmap());
    grayThemeAction->setIcon(QPixmap());
    darkThemeAction->setIcon(QPixmap());
    if (action == lightThemeAction)
    {
        lightThemeAction->setIcon(yesPixmap);
        GlobalVar::colorTheme = 0;
    }
    else if (action == grayThemeAction)
    {
        grayThemeAction->setIcon(yesPixmap);
        GlobalVar::colorTheme = 1;
    }
    else
    {
        darkThemeAction->setIcon(yesPixmap);
        GlobalVar::colorTheme = 2;
    }

    if (GlobalVar::colorTheme == 0)
        changeToLightTheme();
    else if (GlobalVar::colorTheme == 1)
        changeToGrayTheme();
    else
        changeToDarkTheme();

    int count = mainTabWidget->count();
    for (int i = 0; i < count; i++)
    {
        QWidget *widget = mainTabWidget->widget(i);
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        editor->changeColorTheme();
    }

    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    setting.setValue("ColorTheme", GlobalVar::colorTheme);

    infoItemModel->removeRows(0, infoItemModel->rowCount());//删除所有内容
    outputInfoEdit->setPlainText("");
}

void MainWindow::changeToLightTheme()
{
    qApp->setPalette(lightPalette);

    foreach (QMenu *menu, menuList)
    {
        menu->setPalette(lightPalette);
        menu->setStyleSheet(
            "QMenu{background:rgba(245,245,245,1);"//透明度0~1,0表示全透明，1表示不透明
            "border: 2px solid rgb(232,232,232);icon-size: 20px;}"//整个菜单区域的边框粗细、样式、颜色
            "QMenu::item{color:rgba(0,0,0,1);}"
            "QMenu::item:disabled{color:rgba(128,128,128,1);}"
            "QMenu::item:selected{color:black; background-color:rgb(144,200,246);}"
            "QMenu::item:selected:disabled{background-color:rgb(192,192,192);}"
            "QMenu::separator {height:1px; margin:6px 0;background-color: rgba(128, 128, 128, 0.3);}"
            );
    }

    infoTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(192,212,232); font-family:'Consolas','Microsoft Yahei';}");
    infoTableView->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
    infoTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    searchTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(192,232,232); font-family:'Consolas','Microsoft Yahei';}");
    searchTableView->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
    searchTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    outputInfoEdit->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
    outputInfoEdit->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    treeWidget->horizontalScrollBar()->setStyleSheet(GlobalVar::lightHScrollBarStyle);
    treeWidget->verticalScrollBar()->setStyleSheet(GlobalVar::lightVScrollBarStyle);
    treeWidget->header()->setStyleSheet("QHeaderView::section{background:rgb(212,245,255);}");

    QPalette searchTableViewPE=searchTableView->palette();
    searchTableViewPE.setColor(QPalette::Highlight, QColor(160, 128, 128));
    searchTableView->setPalette(searchTableViewPE);
}

void MainWindow::changeToGrayTheme()
{
    qApp->setPalette(grayPalette);

    foreach (QMenu *menu, menuList)
    {
        menu->setPalette(grayPalette);
        menu->setStyleSheet(
            "QMenu{background:rgba(64,64,64,1);"//透明度0~1,0表示全透明，1表示不透明
            "border: 2px solid rgb(96,96,96);icon-size: 20px;}"//整个菜单区域的边框粗细、样式、颜色
            "QMenu::item{color:rgba(255,255,255,1);}"
            "QMenu::item:disabled{color:rgba(128,128,128,1);}"
            "QMenu::item:selected{color:black; background-color:rgb(161,191,222);}"
            "QMenu::item:selected:disabled{background-color:rgb(96,96,96);}"
            "QMenu::separator {height: 1px; margin:6px 0;background-color: rgba(255, 255, 255, 0.3);}"
            );
    }

    infoTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(64,44,24); font-family:'Consolas','Microsoft Yahei';}");
    infoTableView->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    infoTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    searchTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(32,64,96); font-family:'Consolas','Microsoft Yahei';}");
    searchTableView->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    searchTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    outputInfoEdit->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    outputInfoEdit->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    treeWidget->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    treeWidget->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    treeWidget->header()->setStyleSheet("QHeaderView::section{background:rgb(32,64,96);}");

    QPalette searchTableViewPE=searchTableView->palette();
    searchTableViewPE.setColor(QPalette::Highlight, QColor(60, 128, 160));
    searchTableView->setPalette(searchTableViewPE);
}

void MainWindow::changeToDarkTheme()
{
    qApp->setPalette(darkPalette);

    foreach (QMenu *menu, menuList)
    {
        menu->setPalette(darkPalette);
        menu->setStyleSheet(
            "QMenu{background:rgba(64,64,64,1);"//透明度0~1,0表示全透明，1表示不透明
            "border: 2px solid rgb(96,96,96);icon-size: 20px;}"//整个菜单区域的边框粗细、样式、颜色
            "QMenu::item{color:rgba(255,255,255,1);}"
            "QMenu::item:disabled{color:rgba(128,128,128,1);}"
            "QMenu::item:selected{color:black; background-color:rgb(161,191,222);}"
            "QMenu::item:selected:disabled{background-color:rgb(96,96,96);}"
            "QMenu::separator {height: 1px; margin:6px 0;background-color: rgba(255, 255, 255, 0.3);}"
            );
    }

    infoTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(64,44,24); font-family:'Consolas','Microsoft Yahei';}");
    infoTableView->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    infoTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    searchTableView->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(32,64,96); font-family:'Consolas','Microsoft Yahei';}");
    searchTableView->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    searchTableView->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    outputInfoEdit->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    outputInfoEdit->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    treeWidget->horizontalScrollBar()->setStyleSheet(GlobalVar::darkHScrollBarStyle);
    treeWidget->verticalScrollBar()->setStyleSheet(GlobalVar::darkVScrollBarStyle);
    treeWidget->header()->setStyleSheet("QHeaderView::section{background:rgb(32,64,96);}");

    QPalette searchTableViewPE=searchTableView->palette();
    searchTableViewPE.setColor(QPalette::Highlight, QColor(60, 128, 160));
    searchTableView->setPalette(searchTableViewPE);
}

void MainWindow::fullscreenOnOff()
{
    if (this->isFullScreen())
    {
        this->setWindowState(this->windowState()&~Qt::WindowFullScreen);
        fullscreenAction->setText(tr("Fullscreen ON"));
    }
    else
    {
        this->setWindowState(this->windowState()|Qt::WindowFullScreen);
        fullscreenAction->setText(tr("Fullscreen OFF"));
    }
}

void MainWindow::manual()
{
    ManualDialog manualDialog(this);
    manualDialog.exec();
}

void MainWindow::clearRecentActions()
{
    QList<QAction*> actions = historyMenu->actions();
    foreach (QAction* action, actions)
    {
        if (action->objectName() == "recentFileAction")
        {
            historyMenu->removeAction(action);
        }
    }
}

void MainWindow::appExit()
{
    this->close();
}

int MainWindow::getLastWindowState()
{
    return lastWindowState;
}

void MainWindow::tabCloseRequested(int index)
{
    QWidget *closeRequestedWidget = mainTabWidget->widget(index);
    CodeEditor *editor = dynamic_cast<CodeEditor *>(closeRequestedWidget);
    if (editor->document()->isModified())
    {
        mainTabWidget->setCurrentIndex(index);
        QMessageBox::StandardButton result = QMessageBox::question(this, tr("Confirm"), tr("Save changes to %1?").arg(editor->documentTitle()),
                                                                   QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
        if (result == QMessageBox::Yes)
            saveFile(editor);
        else if (result == QMessageBox::Cancel)
            return;
    }

    //获取项目中所有子项的名称列表
    QTreeWidgetItemIterator it(treeWidget);
    QStringList projectSubitemFileList;
    while (*it)
    {
        if ((*it)->parent())
            projectSubitemFileList<<QFileInfo((*it)->data(0, Qt::UserRole).toString()).absoluteFilePath();
        ++it;
    }

    QString fileName = editor->documentTitle();
    QString fileNameAbsolute=QFileInfo(fileName).absoluteFilePath();
    if (!projectSubitemFileList.contains(fileNameAbsolute))//如果文件不属于项目，添加到历史列表中。
        insertRecentFileAction(fileName);

    mainTabWidget->removeTab(index);
    delete editor;
}

void MainWindow::insertRecentFileAction(QString fileName)
{
    if (!QFileInfo::exists(fileName))
        return;
    //删掉重名的最近打开的菜单项
    QList<QAction *> actions = historyMenu->actions();
    foreach (QAction *action, actions)
    {
        if (action->text() == fileName)
            historyMenu->removeAction(action);
    }

    actions.clear();
    actions = historyMenu->actions();
    QAction *recentFileAction = new QAction(fileName);
    connect(recentFileAction, &QAction::triggered, this, &MainWindow::recentFileActionClicked);
    recentFileAction->setObjectName("recentFileAction");
    historyMenu->insertAction(actions[0], recentFileAction);

    actions.clear();
    actions = historyMenu->actions();
    QList<QAction *> recentFileActions;
    foreach (QAction *action, actions)
    {
        if (action->objectName() == "recentFileAction")
            recentFileActions.append(action);
    }

    if (recentFileActions.size() > 16)
    {
        for (int i = 16; i < recentFileActions.size(); i++)
            historyMenu->removeAction(recentFileActions[i]);
    }
}

void MainWindow::recentFileActionClicked()
{
    QAction *action = dynamic_cast<QAction *>(sender());
    openSpecifiedFile(action->text());
}

void MainWindow::currentTabChanged(int index)
{
    if (index < 0)
    {
        saveAction->setEnabled(false);
        saveAsAction->setEnabled(false);
        exportMenu->setEnabled(false);
        cutAction->setEnabled(false);
        deleteAction->setEnabled(false);
        copyAction->setEnabled(false);
        copyAsHtmlAction->setEnabled(false);
        selectAllAction->setEnabled(false);
        indentAction->setEnabled(false);
        unindentAction->setEnabled(false);
        snippetMenu->setEnabled(false);
        snippetToolButton->setEnabled(false);
        commentOnOffAction->setEnabled(false);
        undoAction->setEnabled(false);
        redoAction->setEnabled(false);
        printAction->setEnabled(false);

        foreach (QAction *action, executeMenu->actions())
            action->setEnabled(false);

        foreach (QAction *action, searchMenu->actions())
            action->setEnabled(false);

        formatFileAction->setEnabled(false);

        zoomInAction->setEnabled(false);
        zoomOutAction->setEnabled(false);

        if (findReplaceDialog != nullptr)
            findReplaceDialog->setVisible(false);

        lineLabel->setText("");
        columnLabel->setText("");
        selectedLabel->setText("");
        linesLabel->setText("");

        this->setWindowTitle("Cute C++");
    }
    else
    {
        QWidget *currentWidget = mainTabWidget->widget(index);
        currentWidget->setFocus();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);

        //选中项目管理中相应的文件
        QString fileName=editor->documentTitle();
        QTreeWidgetItemIterator it(treeWidget);
        while (*it)
        {
            if (QFileInfo((*it)->data(0, Qt::UserRole).toString()).absoluteFilePath()==QFileInfo(fileName).absoluteFilePath())
            {
                treeWidget->setCurrentItem((*it));
                break;
            }
            else
                treeWidget->setCurrentItem(nullptr);

            ++it;
        }

        if (editor->document()->isModified())
            saveAction->setEnabled(true);
        else
            saveAction->setEnabled(false);

        formatFileAction->setEnabled(true);

        cutAction->setEnabled(editor->textCursor().hasSelection());
        deleteAction->setEnabled(editor->textCursor().hasSelection());
        copyAction->setEnabled(editor->textCursor().hasSelection());
        copyAsHtmlAction->setEnabled(editor->textCursor().hasSelection());

        selectAllAction->setEnabled(true);

        undoAction->setEnabled(editor->document()->isUndoAvailable());
        redoAction->setEnabled(editor->document()->isRedoAvailable());

        int selectionStart =	editor->textCursor().selectionStart();
        int selectionEnd =	editor->textCursor().selectionEnd();
        QTextBlock textBlockStart = editor->document()->findBlock(selectionStart);
        QTextBlock textBlockEnd = editor->document()->findBlock(selectionEnd);

        if (textBlockStart != textBlockEnd)
        {
            indentAction->setEnabled(true);
            unindentAction->setEnabled(true);
        }
        else
        {
            indentAction->setEnabled(false);
            unindentAction->setEnabled(false);
        }

        snippetToolButton->setEnabled(true);
        snippetMenu->setEnabled(true);
        commentOnOffAction->setEnabled(true);

        exportMenu->setEnabled(true);
        saveAsAction->setEnabled(true);
        printAction->setEnabled(true);

        foreach (QAction *action, executeMenu->actions())
            action->setEnabled(true);

        foreach (QAction *action, searchMenu->actions())
            action->setEnabled(true);

        zoomInAction->setEnabled(true);
        zoomOutAction->setEnabled(true);

        emit editor->cursorPositionChanged();

        this->setWindowTitle("Cute C++ - " + editor->documentTitle());
    }
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    switch (event->key())
    {
    case Qt::Key_Escape: //退出全屏显示
        if (!this->isFullScreen()) break;
        this->setWindowState(this->windowState()&~Qt::WindowFullScreen);
        fullscreenAction->setText(tr("Fullscreen ON"));
        break;
    default:
        break;
    }
}

bool MainWindow::closeAllTabs()
{
    int count = mainTabWidget->count();
    QList<QWidget*> widgetList;
    for (int i = 0; i < count; i++)
        widgetList << mainTabWidget->widget(i);

    foreach (QWidget *widget, widgetList)
    {
        CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
        if (editor->document()->isModified())
        {
            mainTabWidget->setCurrentWidget(widget);
            QMessageBox::StandardButton result = QMessageBox::question(this, tr("Confirm"), tr("Save changes to %1?").arg(editor->documentTitle()), QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel, QMessageBox::Yes);
            if (result == QMessageBox::Yes)
                saveFile(editor);
            else if (result == QMessageBox::Cancel)
                return false;
        }

        QString fileName=QFileInfo(editor->documentTitle()).absoluteFilePath();
        QFileInfo fileInfo(fileName);
        bool isSingleFile=true;
        if (treeWidget->topLevelItemCount()>0)
        {
            //检测是否是单个文件还是项目文件
            QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
            int childCount=topItem->childCount();
            for (int i=0; i<childCount; i++)
            {
                QTreeWidgetItem *subItem=topItem->child(i);
                QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());
                if (subInfo.absoluteFilePath()==fileInfo.absoluteFilePath())
                {
                    isSingleFile=false;//如果是项目文件，不是单文件，退出循环
                    break;
                }
            }
        }

        if (isSingleFile) insertRecentFileAction(fileName);//添加文件名到历史列表

        mainTabWidget->removeTab(mainTabWidget->indexOf(widget));
        delete editor;
    }
    return true;
}

QStringList MainWindow::getDependentHeaders(QString sourceFile)
{
    //Open file and read contents
    bool success=false;
    QString fileString=readStringFromFile(sourceFile, success);
    if (!success) return QStringList{};

    static QRegularExpression re("^[ \t]*#[ \t]*include[ \t]*\"(.+)?\"");
    QRegularExpressionMatch match;
    QStringList lineList=fileString.split('\n');
    QStringList results;
    foreach (QString str, lineList)
    {
        match=re.match(str, 0);

        if (match.hasMatch())
            results<<match.captured(1);
    }

    return results;
}

bool MainWindow::notEqualCompilerSet(CompilerSet set1, CompilerSet set2)
{
    if (set1.modeIndex!=set2.modeIndex) return true;
    if (set1.compileArgCheckBoxState!=set2.compileArgCheckBoxState) return true;
    if (set1.linkArgCheckBoxState!=set2.linkArgCheckBoxState) return true;
    if (set1.staticLinkCheckBoxState!=set2.staticLinkCheckBoxState) return true;
    if (set1.warningAllCheckBoxState!=set2.warningAllCheckBoxState) return true;
    if (set1.warningExtraCheckBoxState!=set2.warningExtraCheckBoxState) return true;

    if (set1.compileArgCheckBoxState&&set2.compileArgCheckBoxState)
        if (set1.compileArg != set2.compileArg) return true;

    if (set1.linkArgCheckBoxState&&set2.linkArgCheckBoxState)
        if (set1.linkArg != set2.linkArg) return true;

    if (set1.binList!=set2.binList) return true;
    if (set1.libList!=set2.libList) return true;
    if (set1.cIncludeList!=set2.cIncludeList) return true;
    if (set1.cppIncludeList!=set2.cppIncludeList) return true;

    if (set1.gccFile != set2.gccFile) return true;
    if (set1.gppFile != set2.gppFile) return true;
    if (set1.makeFile != set2.makeFile) return true;
    if (set1.gdbFile != set2.gdbFile) return true;
    if (set1.gprofFile != set2.gprofFile) return true;
    if (set1.windresFile != set2.windresFile) return true;

    return false;
}

void MainWindow::openedFilesOKButtonClicked()
{
    QString searchString=findReplaceDialog->openedFilesSearchLineEdit->text();
    if (searchString.isEmpty())
    {
        QMessageBox::warning(findReplaceDialog, tr("String to search"), tr("Please input a string to search!"));
        return;
    }

    findReplaceDialog->setVisible(false);//隐藏检索对话框

    //检索前保存修改过的文件
    for (int i=0; i<mainTabWidget->count(); i++)
    {
        QWidget *widget=mainTabWidget->widget(i);
        CodeEditor *editor=dynamic_cast<CodeEditor*>(widget);
        if (editor->document()->isModified())
            saveFile(editor);
    }

    QStringList files;
    for (int i=0; i<mainTabWidget->count(); i++)
    {
        QWidget *widget=mainTabWidget->widget(i);
        if (widget)
        {
            CodeEditor *editor=dynamic_cast<CodeEditor*>(widget);
            QString fileName=editor->documentTitle();
            if (QFile::exists(fileName))
                files<<fileName;
        }
    }

    bool caseSensitive=findReplaceDialog->openedFilesCaseCheckBox->isChecked();
    bool wholeWordsOnly=findReplaceDialog->openedFilesWholeWordsCheckBox->isChecked();

    QMessageBox msg(findReplaceDialog);
    msg.setTextFormat(Qt::PlainText);
    msg.setIcon(QMessageBox::Information);
    msg.setWindowTitle(tr("Search Result"));

    if (findReplaceDialog->openedFilesReplaceCheckBox->isChecked())
    {
        QString replaceString=findReplaceDialog->openedFilesReplaceLineEdit->text();
        int matchedNum=replaceInFiles(searchString, replaceString, files, caseSensitive, wholeWordsOnly);

        if (matchedNum==0)
        {
            msg.setWindowTitle(tr("Search/Replace Result"));
            msg.setText(searchString+"\n\n"+tr("There are no matches found for the above string!"));
            msg.exec();
        }
        else
        {
            msg.setWindowTitle(tr("Search/Replace Result"));
            msg.setText(searchString+"\n\n"+tr("%1 matches of the above string have been replaced by the following string.").arg(matchedNum)+"\n\n"+replaceString);
            msg.exec();
        }
    }
    else
    {
        int matchedNum=searchInFiles(searchString, files, caseSensitive, wholeWordsOnly);

        if (matchedNum==0)
        {
            msg.setText(searchString+"\n\n"+tr("There are no matches found for the above string!"));
            msg.exec();
        }
        else
        {
            msg.setText(searchString+"\n\n"+tr("There are %1 matches found for the above string! ").arg(matchedNum)+"\n"+tr("Results are shown in \"Search Result\" window below."));
            msg.exec();
        }
    }
}

void MainWindow::projectOKButtonClicked()
{
    QString searchString=findReplaceDialog->projectSearchLineEdit->text();
    if (searchString.isEmpty())
    {
        QMessageBox::warning(findReplaceDialog, tr("String to search"), tr("Please input a string to search!"));
        return;
    }

    findReplaceDialog->setVisible(false);//隐藏检索对话框

    if (treeWidget->topLevelItemCount()==0) return;

    QTreeWidgetItem *topItem=treeWidget->topLevelItem(0);
    int childCount=topItem->childCount();
    //保存项目中有修改的文件
    for (int i=0; i<childCount; i++)
    {
        QTreeWidgetItem *subItem=topItem->child(i);
        QFileInfo subInfo(subItem->data(0, Qt::UserRole).toString());

        for (int j = 0; j < mainTabWidget->count(); j++)
        {
            QWidget *widget = mainTabWidget->widget(j);
            CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
            QString docTitle = editor->documentTitle();
            if (QFileInfo(docTitle).absoluteFilePath()==subInfo.absoluteFilePath()&&editor->document()->isModified())
            {
                bool savedSuccessfully = save(editor->toPlainText(), docTitle);
                if (savedSuccessfully)
                    editor->document()->setModified(false);
            }
        }
    }

    //获得项目中所有代码文件的列表
    QStringList files;
    for (int i=0; i<childCount; i++)
    {
        QTreeWidgetItem *subItem=topItem->child(i);
        files<<subItem->data(0, Qt::UserRole).toString();
    }

    bool caseSensitive=findReplaceDialog->projectCaseCheckBox->isChecked();
    bool wholeWordsOnly=findReplaceDialog->projectWholeWordsCheckBox->isChecked();

    QMessageBox msg(findReplaceDialog);
    msg.setTextFormat(Qt::PlainText);
    msg.setIcon(QMessageBox::Information);
    msg.setWindowTitle(tr("Search Result"));

    if (findReplaceDialog->projectReplaceCheckBox->isChecked())
    {
        QString replaceString=findReplaceDialog->projectReplaceLineEdit->text();
        int matchedNum=replaceInFiles(searchString, replaceString, files, caseSensitive, wholeWordsOnly);

        if (matchedNum==0)
        {
            msg.setWindowTitle(tr("Search/Replace Result"));
            msg.setText(searchString+"\n\n"+tr("There are no matches found for the above string!"));
            msg.exec();
        }
        else
        {
            msg.setWindowTitle(tr("Search/Replace Result"));
            msg.setText(searchString+"\n\n"+tr("%1 matches of the above string have been replaced by the following string.").arg(matchedNum)+"\n\n"+replaceString);
            msg.exec();
        }
    }
    else
    {
        int matchedNum=searchInFiles(searchString, files, caseSensitive, wholeWordsOnly);

        if (matchedNum==0)
        {
            msg.setText(searchString+"\n\n"+tr("There are no matches found for the above string!"));
            msg.exec();
        }
        else
        {
            msg.setText(searchString+"\n\n"+tr("There are %1 matches found for the above string! ").arg(matchedNum)+"\n"+tr("Results are shown in \"Search Result\" window below."));
            msg.exec();
        }
    }
}

int MainWindow::replaceInFiles(QString searchString, QString replaceString, QStringList files, bool caseSensitive, bool wholeWordsOnly)
{
    searchString=QRegularExpression::escape(searchString);

    if (wholeWordsOnly)
        searchString="\\b"+searchString+"\\b";

    QRegularExpression regularExp(searchString, (caseSensitive?QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption));

    QStringList otherFiles=files;
    int replacedNum=0;
    int tabCount=mainTabWidget->count();
    //如果是打开的文件，则直接替换，不需要读取文件
    foreach (QString fileName, files)
    {
        for (int i = 0; i < tabCount; i++)
        {
            QWidget *widget = mainTabWidget->widget(i);
            CodeEditor *editor = dynamic_cast<CodeEditor *>(widget);
            if (fileName.compare(editor->documentTitle(), Qt::CaseInsensitive)==0)
            {
                otherFiles.removeAll(fileName);
                QString codeStr=editor->toPlainText();
                int matchNum=codeStr.count(regularExp);
                replacedNum+=matchNum;
                if (matchNum>0)
                {
                    codeStr.replace(regularExp, replaceString);

                    //记住水平和垂直滚动条的位置，方便格式化以后恢复位置
                    int vScrollValue = editor->verticalScrollBar()->value();
                    int hScrollValue = editor->horizontalScrollBar()->value();

                    QTextCursor textCursor = editor->textCursor();
                    int blockNumber = textCursor.blockNumber(); //当前段落号
                    int positionInBlock = textCursor.positionInBlock(); //光标在当前段落的位置

                    textCursor.beginEditBlock();
                    textCursor.select(QTextCursor::Document);
                    textCursor.removeSelectedText();
                    editor->appendPlainText(codeStr);//如果用setPlainText会导致documentTitle消失，且无法undo

                    //按照段落号查询到格式化后的段落
                    QTextBlock textBlock;
                    if (blockNumber < editor->document()->blockCount())
                        textBlock = editor->document()->findBlockByLineNumber(blockNumber);
                    else
                        textBlock = editor->document()->lastBlock();
                    //格式化后光标应该的位置
                    int newPosition = textBlock.position() + positionInBlock;
                    if (newPosition >= editor->document()->characterCount())
                        newPosition = editor->document()->characterCount() - 1;

                    //设定光标新位置，结束编辑，并更新光标位置
                    textCursor.setPosition(newPosition, QTextCursor::MoveAnchor);
                    //恢复格式化前的滚动条位置
                    editor->verticalScrollBar()->setValue(vScrollValue);
                    editor->horizontalScrollBar()->setValue(hScrollValue);
                    textCursor.insertText(" ");//插入一个空字符，然后删除，是为了能够redo到光标处。
                    textCursor.deletePreviousChar();

                    editor->setTextCursor(textCursor);
                    textCursor.endEditBlock();
                    //editor->setCenterOnScroll(false);//取消光标保持在窗口中心的功能，防止产生多余的滚动范围
                }
            }
        }
    }

    //如果是未打开的文件，则读取以后进行置换，然后保存。
    foreach (QString fileName, otherFiles)
    {
        for (int i = 0; i < tabCount; i++)
        {
            bool success=false;
            QString codeStr=readStringFromFile(fileName, success);
            if (success)
            {
                int matchNum=codeStr.count(regularExp);
                replacedNum+=matchNum;
                if (matchNum>0)
                {
                    codeStr.replace(regularExp, replaceString);
                    bool saveOK=save(codeStr, fileName);
                    if (!saveOK)
                        QMessageBox::warning(this, tr("Error in file saving"), fileName + "\n\n"+tr("Failed in saving the above file!"));
                }
            }
        }
    }

    return replacedNum;
}

int MainWindow::searchInFiles(QString searchString, QStringList files, bool caseSensitive, bool wholeWordsOnly)
{
    //清空检索结果框数据
    searchItemModel->removeRows(0, searchItemModel->rowCount());
    searchTableView->reset();
    searchTableView->repaint();
    searchTableView->setModel(searchItemModel);
    searchTableView->setColumnWidth(0, 100); //设置第0列宽度
    searchTableView->setColumnWidth(3, 200); //设置第3列宽度

    searchString=QRegularExpression::escape(searchString);

    if (wholeWordsOnly)
        searchString="\\b"+searchString+"\\b";

    int item1MaxLength=0;
    int item4MaxLength=0;

    searchTableView->setUpdatesEnabled(false);

    QRegularExpression regularExp(searchString, (caseSensitive?QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption));
    QRegularExpressionMatch match;
    QPlainTextEdit textEditor;
    QVector<SearchResult> results;
    SearchResult result;

    foreach (QString fileName, files)
    {
        bool success=false;
        QString str=readStringFromFile(fileName, success);
        if (success&&!str.isEmpty())
        {
            textEditor.setPlainText(str);
            QRegularExpressionMatchIterator matchIterator = regularExp.globalMatch(str);

            while (matchIterator.hasNext())
            {
                match = matchIterator.next();
                QTextCursor textCursor=textEditor.textCursor();
                textCursor.setPosition(match.capturedStart(), QTextCursor::MoveAnchor);
                int lineNum=textCursor.blockNumber() + 1;
                int columnNum=textCursor.columnNumber() + 1;
                QString blockText=textEditor.document()->findBlockByLineNumber(lineNum-1).text();

                result.fileName=fileName;

                int item1Length=QFileInfo(fileName).fileName().toLocal8Bit().length();
                if (item1Length>item1MaxLength)
                    item1MaxLength=item1Length;

                result.row=lineNum;
                result.column=columnNum;

                RichLineCode lineCode{blockText,columnNum-1,(int)match.capturedLength()};
                QVariant variant;
                variant.setValue(lineCode);
                result.richLine=variant;

                results<<result;

                QString item4Text=blockText.toLocal8Bit().replace("\t", "    ");
                int item4Length=item4Text.length();
                if (item4Length>item4MaxLength)
                    item4MaxLength=item4Length;
            }
        }
    }

    int resultNum=results.size();
    searchItemModel->setColumnCount(4);
    searchItemModel->setRowCount(resultNum);

    for (int i=0; i<resultNum; i++)
    {
        searchItemModel->setData(searchItemModel->index(i, 0), QFileInfo(results[i].fileName).fileName(), Qt::DisplayRole);
        searchItemModel->setData(searchItemModel->index(i, 0), results[i].fileName, Qt::ToolTipRole);
        searchItemModel->setData(searchItemModel->index(i, 0), searchString, Qt::WhatsThisRole);
        searchItemModel->setData(searchItemModel->index(i, 0), caseSensitive, Qt::UserRole);

        searchItemModel->setData(searchItemModel->index(i, 1), results[i].row, Qt::DisplayRole);
        QStandardItem * item=searchItemModel->item(i,1);
        item->setTextAlignment(Qt::AlignCenter);

        searchItemModel->setData(searchItemModel->index(i, 2), results[i].column, Qt::DisplayRole);
        item=searchItemModel->item(i,2);
        item->setTextAlignment(Qt::AlignCenter);

        searchItemModel->setData(searchItemModel->index(i, 3), results[i].richLine, Qt::WhatsThisRole);
    }

    searchTableView->setUpdatesEnabled(true);

    QFontMetrics fm(searchTableView->font());
    double charWidth = fm.horizontalAdvance("W")+0.5;
    double item1MaxWidth=charWidth*item1MaxLength+30;
    double item4MaxWidth=charWidth*item4MaxLength+60;

    searchTableView->setColumnWidth(0, item1MaxWidth); //设置第0列宽度
    searchTableView->setColumnWidth(3, item4MaxWidth); //设置第3列宽度

    if (resultNum>0)
        infoTabWidget->setCurrentIndex(2);

    if (searchTableView->model()->rowCount()>0)
        tableViewDoubleClicked(searchItemModel->index(0,1));

    return resultNum;
}

void MainWindow::closeEvent(QCloseEvent *event)
{
    QString appDataPath = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
    QSettings setting(appDataPath + "/Setting.ini", QSettings::IniFormat);

    setting.setValue("LastWindowState", QString::number(this->windowState()));
    setting.setValue("LastWindowWidth", this->normalGeometry().width());
    setting.setValue("LastWindowHeight", this->normalGeometry().height());
    setting.setValue("ToolBarArea", this->toolBarArea(mainToolBar));
    setting.setValue("ShowToolBar", !toolBarAction->icon().isNull());
    setting.setValue("ShowStatusBar", !statusBarAction->icon().isNull());

    if (mainTabWidget->count() > 0)
    {
        QWidget *currentWidget = mainTabWidget->currentWidget();
        CodeEditor *editor = dynamic_cast<CodeEditor *>(currentWidget);
        setting.setValue("EditorFontSize", editor->font().pointSize());
    }

    if (!closeAllTabs())
    {
        event->ignore();
        return;
    }

    //追加项目名称到历史列表
    int topLevelItemCount=treeWidget->topLevelItemCount();
    QList<QTreeWidgetItem*> topLevelItemList;
    for (int i=0; i<topLevelItemCount; i++)
        topLevelItemList<<treeWidget->topLevelItem(i);

    foreach (QTreeWidgetItem *item, topLevelItemList)//关闭所有项目，并追加项目名到历史列表
    {
        QString projectFile=item->data(0, Qt::UserRole).toString();
        insertRecentFileAction(projectFile);//添加文件名到历史列表
        closeProject(item);
    }

    QStringList recentFiles;
    QList<QAction *> actions = historyMenu->actions();
    foreach (QAction *action, actions)
    {
        if (action->objectName() == "recentFileAction")
            recentFiles.append(action->text());
    }

    setting.setValue("RecentFiles", recentFiles);

    QVariant variant;
    variant.setValue(vSplitter->sizes());
    setting.setValue("vSplitterSizes", variant);
    variant.setValue(hSplitter->sizes());
    setting.setValue("hSplitterSizes", variant);

    qApp->quit();
}

bool MainWindow::programHasConsole(const QString & filename)
{
    bool result = true;//默认有console窗口

    std::wstring wlpstrstd = filename.toStdWString();
    LPCWSTR lpcwStr = wlpstrstd.c_str();
    HANDLE handle = CreateFile(lpcwStr, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);

    if (handle != INVALID_HANDLE_VALUE)
    {
        IMAGE_DOS_HEADER dos_header;
        DWORD signature;
        DWORD bytesread;
        IMAGE_FILE_HEADER pe_header;
        IMAGE_OPTIONAL_HEADER opt_header;

        ReadFile(handle, &dos_header, sizeof(dos_header), &bytesread, NULL);
        SetFilePointer(handle, dos_header.e_lfanew, NULL, 0);
        ReadFile(handle, &signature, sizeof(signature), &bytesread, NULL);
        ReadFile(handle, &pe_header, sizeof(pe_header), &bytesread, NULL);
        ReadFile(handle, &opt_header, sizeof(opt_header), &bytesread, NULL);

        result = (opt_header.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI);//确认是否有console窗口
    }

    CloseHandle(handle);

    return result;
}
